Skip to content

Commit

Permalink
Merge pull request #17 from Innoptech/fix-windows-ci
Browse files Browse the repository at this point in the history
Add a activateOverflowSafety to activate/deactivate buffer overflow safety.
  • Loading branch information
jeanchristopheruel authored Oct 15, 2024
2 parents fb0cfce + 05a774c commit 81a78d5
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 14 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ faces = [
triangles = openstl.convert.triangles(vertices, faces)
```

### Read large STL file
To read large STL file with a trianlge count > **1 000 000**, the openstl buffer overflow safety must be unactivated with
`openstl.set_activate_overflow_safety(False)` after import. Deactivating overflow safety may expose the application
to potential buffer overflow risks (if openstl is used in a backend server with sensible data for example).

# C++ Usage
### Read STL from file
```c++
Expand Down
26 changes: 16 additions & 10 deletions modules/core/include/openstl/core/stl.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ SOFTWARE.
#include <cmath>
#include <algorithm>

#define MAX_TRIANGLES 1000000

namespace openstl
{
// Disable padding for the structure
Expand Down Expand Up @@ -126,6 +128,15 @@ namespace openstl
// Deserialize
//---------------------------------------------------------------------------------------------------------

/**
* A library-level configuration to activate/deactivate the buffer overflow safety
* @return
*/
bool& activateOverflowSafety() {
static bool safety_enabled = true;
return safety_enabled;
}

/**
* @brief Read a vertex from a stream.
*
Expand Down Expand Up @@ -172,6 +183,9 @@ namespace openstl
readVertex(stream, tri.v2);
triangles.push_back(tri);
}
if (activateOverflowSafety() && triangles.size() > MAX_TRIANGLES) {
throw std::runtime_error("Triangle count exceeds the maximum allowable value.");
}
}
return triangles;
}
Expand All @@ -185,48 +199,40 @@ namespace openstl
*/
template <typename Stream>
std::vector<Triangle> deserializeBinaryStl(Stream& stream) {
// Get the current position and determine the file size
auto start_pos = stream.tellg();
stream.seekg(0, std::ios::end);
auto end_pos = stream.tellg();
stream.seekg(start_pos);

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

// Explicitly read the header (80 bytes)
char header[80];
stream.read(header, sizeof(header));

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

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

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

// Validate triangle count
const uint32_t MAX_TRIANGLES = 1000000;
if (triangle_qty > MAX_TRIANGLES) {
// Apply the triangle count limit only if activateOverflowSafety is true
if (activateOverflowSafety() && triangle_qty > MAX_TRIANGLES) {
throw std::runtime_error("Triangle count exceeds the maximum allowable value.");
}

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

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

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

Expand Down
13 changes: 12 additions & 1 deletion python/core/src/stl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,19 @@ namespace pybind11 { namespace detail {
}} // namespace pybind11::detail



void serialize(py::module_ &m) {
// Define getter and setter for the activateOverflowSafety option
m.def("get_activate_overflow_safety", []() {
return activateOverflowSafety();
});

m.def("set_activate_overflow_safety", [](bool value) {
if (!value) {
py::print("Warning: Deactivating overflow safety may expose the application to potential buffer overflow risks.",
py::module_::import("sys").attr("stderr"));
}
activateOverflowSafety() = value;
});

py::enum_<StlFormat>(m, "format")
.value("ascii", StlFormat::ASCII)
Expand Down
18 changes: 15 additions & 3 deletions tests/core/src/deserialize.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ TEST_CASE("Binary STL Serialization/Deserialization Security and Integrity Tests
CHECK_THROWS_AS(deserializeBinaryStl(file), std::runtime_error);
}
SECTION("Test deserialization with the maximum number of triangles") {
const uint32_t MAX_TRIANGLES = 1000000;
const std::string filename = "max_triangles.stl";

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

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

std::ifstream file(filename, std::ios::binary);
Expand All @@ -190,6 +188,20 @@ TEST_CASE("Binary STL Serialization/Deserialization Security and Integrity Tests
// Test that deserialization throws an exception for exceeding MAX_TRIANGLES
CHECK_THROWS_AS(deserializeBinaryStl(file), std::runtime_error);
}
SECTION("Test deserialization exceeding the maximum number of triangles with deactivated safety") {
const std::string filename = "exceeding_triangles.stl";

// Create a file with more than MAX_TRIANGLES triangles
std::vector<Triangle> triangles(MAX_TRIANGLES+1);
testutils::createStlWithTriangles(triangles, filename);

std::ifstream file(filename, std::ios::binary);
REQUIRE(file.is_open());

// Deactivate buffer overflow safety
activateOverflowSafety() = false;
CHECK_NOTHROW(deserializeBinaryStl(file));
}
SECTION("Test deserialization with an empty file") {
const std::string filename{"empty_triangles.stl"};
testutils::createEmptyStlFile(filename); // Generate an empty file
Expand Down

0 comments on commit 81a78d5

Please sign in to comment.