Route Grid.compute_fov algorithm through PyFOV::from_arg
The compute_fov binding parsed its `algorithm` argument as a raw int and cast it directly to TCOD_fov_algorithm_t. Out-of-range values (e.g. -49 reinterpreted as 4294967247) triggered UBSan's "invalid enum load" deep in GridData::computeFOV. PyFOV::from_arg already does the right validation for every other algorithm entry point: accepts the FOV IntEnum, ints in [0, NB_FOV_ALGORITHMS), or None (default BASIC); raises ValueError otherwise. Route the binding through it. Fuzz corpus crash tests/fuzz/crashes/fov-crash-d5da064d802ae2b5691c520907cd692d04de8bb2 now runs cleanly. closes #310 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4fd718472a
commit
19b43ce5fa
2 changed files with 82 additions and 4 deletions
|
|
@ -14,6 +14,7 @@
|
|||
#include "EntityBehavior.h"
|
||||
#include "PyTrigger.h"
|
||||
#include "UIBase.h"
|
||||
#include "PyFOV.h"
|
||||
|
||||
// =========================================================================
|
||||
// Cell access: py_at, subscript, mpmethods
|
||||
|
|
@ -97,10 +98,10 @@ PyObject* UIGrid::py_compute_fov(PyUIGridObject* self, PyObject* args, PyObject*
|
|||
PyObject* pos_obj = NULL;
|
||||
int radius = 0;
|
||||
int light_walls = 1;
|
||||
int algorithm = FOV_BASIC;
|
||||
PyObject* algorithm_obj = NULL;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ipi", const_cast<char**>(kwlist),
|
||||
&pos_obj, &radius, &light_walls, &algorithm)) {
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ipO", const_cast<char**>(kwlist),
|
||||
&pos_obj, &radius, &light_walls, &algorithm_obj)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +110,17 @@ PyObject* UIGrid::py_compute_fov(PyUIGridObject* self, PyObject* args, PyObject*
|
|||
return NULL;
|
||||
}
|
||||
|
||||
self->data->computeFOV(x, y, radius, light_walls, (TCOD_fov_algorithm_t)algorithm);
|
||||
// #310: validate algorithm via PyFOV::from_arg so out-of-range ints become a
|
||||
// ValueError at the Python boundary instead of a UBSan-flagged invalid enum
|
||||
// load deep in GridData::computeFOV.
|
||||
TCOD_fov_algorithm_t algorithm = FOV_BASIC;
|
||||
if (algorithm_obj != NULL) {
|
||||
if (!PyFOV::from_arg(algorithm_obj, &algorithm)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
self->data->computeFOV(x, y, radius, light_walls, algorithm);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
|
|
|||
67
tests/regression/issue_310_compute_fov_enum_test.py
Normal file
67
tests/regression/issue_310_compute_fov_enum_test.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
"""Regression test for issue #310.
|
||||
|
||||
Grid.compute_fov's algorithm argument was passed directly as an int into
|
||||
GridData::computeFOV, where it was cast to TCOD_fov_algorithm_t. Values
|
||||
outside the enum range caused UBSan to report an invalid enum load. Fuzz
|
||||
target fuzz_fov surfaced this with algorithm=-49.
|
||||
|
||||
The fix routes the argument through PyFOV::from_arg, which accepts FOV
|
||||
enum members, ints in [0, NB_FOV_ALGORITHMS), or None (defaults to BASIC),
|
||||
and raises ValueError for out-of-range ints.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||||
|
||||
# Valid paths: enum, int, None, omitted
|
||||
grid.compute_fov((5, 5), radius=3, algorithm=mcrfpy.FOV.BASIC)
|
||||
grid.compute_fov((5, 5), radius=3, algorithm=0) # FOV_BASIC
|
||||
grid.compute_fov((5, 5), radius=3, algorithm=None) # default
|
||||
grid.compute_fov((5, 5), radius=3) # default, no arg
|
||||
|
||||
# Out-of-range negative int (this was the UBSan trigger)
|
||||
try:
|
||||
grid.compute_fov((5, 5), radius=3, algorithm=-49)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
print("FAIL: algorithm=-49 should raise ValueError")
|
||||
sys.exit(1)
|
||||
|
||||
# Out-of-range positive int (above NB_FOV_ALGORITHMS sentinel)
|
||||
try:
|
||||
grid.compute_fov((5, 5), radius=3, algorithm=9999)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
print("FAIL: algorithm=9999 should raise ValueError")
|
||||
sys.exit(1)
|
||||
|
||||
# Wrong type still rejected
|
||||
try:
|
||||
grid.compute_fov((5, 5), radius=3, algorithm="basic")
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
else:
|
||||
print("FAIL: algorithm='basic' should raise TypeError")
|
||||
sys.exit(1)
|
||||
|
||||
# Boundary int -1 should fail (negative)
|
||||
try:
|
||||
grid.compute_fov((5, 5), radius=3, algorithm=-1)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
print("FAIL: algorithm=-1 should raise ValueError")
|
||||
sys.exit(1)
|
||||
|
||||
print("PASS")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue