Skip to content

Add a activateOverflowSafety to activate/deactivate buffer overflow safety. #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading