HeightMap: add GRID_MAX limit and input validation
Fixes potential integer overflow and invalid input issues: - Add GRID_MAX constant (8192) to Common.h for global use - Validate HeightMap dimensions against GRID_MAX to prevent integer overflow in w*h calculations (65536*65536 = 0) - Add min > max validation for clamp() and normalize() - Add unit tests for all new validation cases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c095be4b73
commit
87444c2fd0
3 changed files with 90 additions and 0 deletions
|
|
@ -2,6 +2,10 @@
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <SFML/Audio.hpp>
|
#include <SFML/Audio.hpp>
|
||||||
|
|
||||||
|
// Maximum dimension for grids, layers, and heightmaps (8192x8192 = 256MB of float data)
|
||||||
|
// Prevents integer overflow in size calculations and limits memory allocation
|
||||||
|
constexpr int GRID_MAX = 8192;
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,13 @@ int PyHeightMap::init(PyHeightMapObject* self, PyObject* args, PyObject* kwds)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (width > GRID_MAX || height > GRID_MAX) {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"HeightMap dimensions cannot exceed %d (got %dx%d)",
|
||||||
|
GRID_MAX, width, height);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up any existing heightmap
|
// Clean up any existing heightmap
|
||||||
if (self->heightmap) {
|
if (self->heightmap) {
|
||||||
TCOD_heightmap_delete(self->heightmap);
|
TCOD_heightmap_delete(self->heightmap);
|
||||||
|
|
@ -253,6 +260,11 @@ PyObject* PyHeightMap::clamp(PyHeightMapObject* self, PyObject* args, PyObject*
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (min_val > max_val) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "min must be less than or equal to max");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
TCOD_heightmap_clamp(self->heightmap, min_val, max_val);
|
TCOD_heightmap_clamp(self->heightmap, min_val, max_val);
|
||||||
|
|
||||||
// Return self for chaining
|
// Return self for chaining
|
||||||
|
|
@ -277,6 +289,11 @@ PyObject* PyHeightMap::normalize(PyHeightMapObject* self, PyObject* args, PyObje
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (min_val > max_val) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "min must be less than or equal to max");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
TCOD_heightmap_normalize(self->heightmap, min_val, max_val);
|
TCOD_heightmap_normalize(self->heightmap, min_val, max_val);
|
||||||
|
|
||||||
// Return self for chaining
|
// Return self for chaining
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,71 @@ def test_invalid_size_type():
|
||||||
print("PASS: test_invalid_size_type")
|
print("PASS: test_invalid_size_type")
|
||||||
|
|
||||||
|
|
||||||
|
def test_size_exceeds_grid_max():
|
||||||
|
"""Size exceeding GRID_MAX (8192) raises ValueError"""
|
||||||
|
# Test width exceeds limit
|
||||||
|
try:
|
||||||
|
mcrfpy.HeightMap((10000, 100))
|
||||||
|
print("FAIL: test_size_exceeds_grid_max - should have raised ValueError for width=10000")
|
||||||
|
sys.exit(1)
|
||||||
|
except ValueError as e:
|
||||||
|
assert "8192" in str(e) or "cannot exceed" in str(e).lower()
|
||||||
|
|
||||||
|
# Test height exceeds limit
|
||||||
|
try:
|
||||||
|
mcrfpy.HeightMap((100, 10000))
|
||||||
|
print("FAIL: test_size_exceeds_grid_max - should have raised ValueError for height=10000")
|
||||||
|
sys.exit(1)
|
||||||
|
except ValueError as e:
|
||||||
|
assert "8192" in str(e) or "cannot exceed" in str(e).lower()
|
||||||
|
|
||||||
|
# Test both exceed limit (would cause integer overflow without validation)
|
||||||
|
try:
|
||||||
|
mcrfpy.HeightMap((65536, 65536))
|
||||||
|
print("FAIL: test_size_exceeds_grid_max - should have raised ValueError for 65536x65536")
|
||||||
|
sys.exit(1)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("PASS: test_size_exceeds_grid_max")
|
||||||
|
|
||||||
|
|
||||||
|
def test_clamp_min_greater_than_max():
|
||||||
|
"""clamp() with min > max raises ValueError"""
|
||||||
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||||||
|
try:
|
||||||
|
hmap.clamp(min=1.0, max=0.0)
|
||||||
|
print("FAIL: test_clamp_min_greater_than_max - should have raised ValueError")
|
||||||
|
sys.exit(1)
|
||||||
|
except ValueError as e:
|
||||||
|
assert "min" in str(e).lower() and "max" in str(e).lower()
|
||||||
|
print("PASS: test_clamp_min_greater_than_max")
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_min_greater_than_max():
|
||||||
|
"""normalize() with min > max raises ValueError"""
|
||||||
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||||||
|
try:
|
||||||
|
hmap.normalize(min=1.0, max=0.0)
|
||||||
|
print("FAIL: test_normalize_min_greater_than_max - should have raised ValueError")
|
||||||
|
sys.exit(1)
|
||||||
|
except ValueError as e:
|
||||||
|
assert "min" in str(e).lower() and "max" in str(e).lower()
|
||||||
|
print("PASS: test_normalize_min_greater_than_max")
|
||||||
|
|
||||||
|
|
||||||
|
def test_max_valid_size():
|
||||||
|
"""Size at GRID_MAX boundary works"""
|
||||||
|
# Test at the exact limit - this should work
|
||||||
|
hmap = mcrfpy.HeightMap((8192, 1))
|
||||||
|
assert hmap.size == (8192, 1)
|
||||||
|
|
||||||
|
hmap2 = mcrfpy.HeightMap((1, 8192))
|
||||||
|
assert hmap2.size == (1, 8192)
|
||||||
|
|
||||||
|
print("PASS: test_max_valid_size")
|
||||||
|
|
||||||
|
|
||||||
def run_all_tests():
|
def run_all_tests():
|
||||||
"""Run all tests"""
|
"""Run all tests"""
|
||||||
print("Running HeightMap basic tests...")
|
print("Running HeightMap basic tests...")
|
||||||
|
|
@ -189,6 +254,10 @@ def run_all_tests():
|
||||||
test_repr()
|
test_repr()
|
||||||
test_invalid_size()
|
test_invalid_size()
|
||||||
test_invalid_size_type()
|
test_invalid_size_type()
|
||||||
|
test_size_exceeds_grid_max()
|
||||||
|
test_clamp_min_greater_than_max()
|
||||||
|
test_normalize_min_greater_than_max()
|
||||||
|
test_max_valid_size()
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print("All HeightMap basic tests PASSED!")
|
print("All HeightMap basic tests PASSED!")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue