Skip to content

Commit 81a78d5

Browse files
Merge pull request #17 from Innoptech/fix-windows-ci
Add a activateOverflowSafety to activate/deactivate buffer overflow safety.
2 parents fb0cfce + 05a774c commit 81a78d5

File tree

4 files changed

+48
-14
lines changed

4 files changed

+48
-14
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ faces = [
113113
triangles = openstl.convert.triangles(vertices, faces)
114114
```
115115

116+
### Read large STL file
117+
To read large STL file with a trianlge count > **1 000 000**, the openstl buffer overflow safety must be unactivated with
118+
`openstl.set_activate_overflow_safety(False)` after import. Deactivating overflow safety may expose the application
119+
to potential buffer overflow risks (if openstl is used in a backend server with sensible data for example).
120+
116121
# C++ Usage
117122
### Read STL from file
118123
```c++

modules/core/include/openstl/core/stl.h

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ SOFTWARE.
3434
#include <cmath>
3535
#include <algorithm>
3636

37+
#define MAX_TRIANGLES 1000000
38+
3739
namespace openstl
3840
{
3941
// Disable padding for the structure
@@ -126,6 +128,15 @@ namespace openstl
126128
// Deserialize
127129
//---------------------------------------------------------------------------------------------------------
128130

131+
/**
132+
* A library-level configuration to activate/deactivate the buffer overflow safety
133+
* @return
134+
*/
135+
bool& activateOverflowSafety() {
136+
static bool safety_enabled = true;
137+
return safety_enabled;
138+
}
139+
129140
/**
130141
* @brief Read a vertex from a stream.
131142
*
@@ -172,6 +183,9 @@ namespace openstl
172183
readVertex(stream, tri.v2);
173184
triangles.push_back(tri);
174185
}
186+
if (activateOverflowSafety() && triangles.size() > MAX_TRIANGLES) {
187+
throw std::runtime_error("Triangle count exceeds the maximum allowable value.");
188+
}
175189
}
176190
return triangles;
177191
}
@@ -185,48 +199,40 @@ namespace openstl
185199
*/
186200
template <typename Stream>
187201
std::vector<Triangle> deserializeBinaryStl(Stream& stream) {
188-
// Get the current position and determine the file size
189202
auto start_pos = stream.tellg();
190203
stream.seekg(0, std::ios::end);
191204
auto end_pos = stream.tellg();
192205
stream.seekg(start_pos);
193206

194-
// Ensure the file is large enough for the header and triangle count
195207
if (end_pos - start_pos < 84) {
196208
throw std::runtime_error("File is too small to be a valid STL file.");
197209
}
198210

199-
// Explicitly read the header (80 bytes)
200211
char header[80];
201212
stream.read(header, sizeof(header));
202213

203214
if (stream.gcount() != sizeof(header)) {
204215
throw std::runtime_error("Failed to read the full header. Possible corruption or incomplete file.");
205216
}
206217

207-
// Read and validate triangle count (4 bytes)
208218
uint32_t triangle_qty;
209219
stream.read(reinterpret_cast<char*>(&triangle_qty), sizeof(triangle_qty));
210220

211221
if (stream.gcount() != sizeof(triangle_qty) || stream.fail() || stream.eof()) {
212222
throw std::runtime_error("Failed to read the triangle count. Possible corruption or incomplete file.");
213223
}
214224

215-
// Validate triangle count
216-
const uint32_t MAX_TRIANGLES = 1000000;
217-
if (triangle_qty > MAX_TRIANGLES) {
225+
// Apply the triangle count limit only if activateOverflowSafety is true
226+
if (activateOverflowSafety() && triangle_qty > MAX_TRIANGLES) {
218227
throw std::runtime_error("Triangle count exceeds the maximum allowable value.");
219228
}
220229

221-
// Calculate the expected size of the triangle data
222230
std::size_t expected_data_size = sizeof(Triangle) * triangle_qty;
223231

224-
// Ensure the stream has enough data left
225232
if (end_pos - stream.tellg() < static_cast<std::streamoff>(expected_data_size)) {
226233
throw std::runtime_error("Not enough data in stream for the expected triangle count.");
227234
}
228235

229-
// Read triangles
230236
std::vector<Triangle> triangles(triangle_qty);
231237
stream.read(reinterpret_cast<char*>(triangles.data()), expected_data_size);
232238

python/core/src/stl.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,19 @@ namespace pybind11 { namespace detail {
104104
}} // namespace pybind11::detail
105105

106106

107-
108107
void serialize(py::module_ &m) {
108+
// Define getter and setter for the activateOverflowSafety option
109+
m.def("get_activate_overflow_safety", []() {
110+
return activateOverflowSafety();
111+
});
112+
113+
m.def("set_activate_overflow_safety", [](bool value) {
114+
if (!value) {
115+
py::print("Warning: Deactivating overflow safety may expose the application to potential buffer overflow risks.",
116+
py::module_::import("sys").attr("stderr"));
117+
}
118+
activateOverflowSafety() = value;
119+
});
109120

110121
py::enum_<StlFormat>(m, "format")
111122
.value("ascii", StlFormat::ASCII)

tests/core/src/deserialize.test.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ TEST_CASE("Binary STL Serialization/Deserialization Security and Integrity Tests
161161
CHECK_THROWS_AS(deserializeBinaryStl(file), std::runtime_error);
162162
}
163163
SECTION("Test deserialization with the maximum number of triangles") {
164-
const uint32_t MAX_TRIANGLES = 1000000;
165164
const std::string filename = "max_triangles.stl";
166165

167166
// Create a file with exactly MAX_TRIANGLES triangles
@@ -177,11 +176,10 @@ TEST_CASE("Binary STL Serialization/Deserialization Security and Integrity Tests
177176
REQUIRE(deserialized_triangles.size() == MAX_TRIANGLES);
178177
}
179178
SECTION("Test deserialization exceeding the maximum number of triangles") {
180-
const uint32_t EXCEEDING_TRIANGLES = 1'000'001;
181179
const std::string filename = "exceeding_triangles.stl";
182180

183181
// Create a file with more than MAX_TRIANGLES triangles
184-
std::vector<Triangle> triangles(EXCEEDING_TRIANGLES);
182+
std::vector<Triangle> triangles(MAX_TRIANGLES+1);
185183
testutils::createStlWithTriangles(triangles, filename);
186184

187185
std::ifstream file(filename, std::ios::binary);
@@ -190,6 +188,20 @@ TEST_CASE("Binary STL Serialization/Deserialization Security and Integrity Tests
190188
// Test that deserialization throws an exception for exceeding MAX_TRIANGLES
191189
CHECK_THROWS_AS(deserializeBinaryStl(file), std::runtime_error);
192190
}
191+
SECTION("Test deserialization exceeding the maximum number of triangles with deactivated safety") {
192+
const std::string filename = "exceeding_triangles.stl";
193+
194+
// Create a file with more than MAX_TRIANGLES triangles
195+
std::vector<Triangle> triangles(MAX_TRIANGLES+1);
196+
testutils::createStlWithTriangles(triangles, filename);
197+
198+
std::ifstream file(filename, std::ios::binary);
199+
REQUIRE(file.is_open());
200+
201+
// Deactivate buffer overflow safety
202+
activateOverflowSafety() = false;
203+
CHECK_NOTHROW(deserializeBinaryStl(file));
204+
}
193205
SECTION("Test deserialization with an empty file") {
194206
const std::string filename{"empty_triangles.stl"};
195207
testutils::createEmptyStlFile(filename); // Generate an empty file

0 commit comments

Comments
 (0)