Fix #109024: Off-by-1 in rna_access for non-array props without raw access
The `a + array_len > in.len` check was off-by-1 whenever accessing a non-array property without raw access. This was because `array_len` was actually the array length of the property, which is `0` for non-array properties. Given an array which was too short, this would cause the slower loop to overrun the end of the array by one item. When getting items this would cause a crash on a debug build with `Fatal Python error: _PyMem_DebugRawFree: bad trailing pad byte`. So use `item_len` instead, wichi is always set to `1` for non-array properties. Also do not assume that an `array_len` of `0` means that the property is an array. While this may be true currently, it is cleaner and safer to use the dedicated RNA API to check that. This PR also adds some basic checks for expected failure of `foreach_set` /`foreach_get` API when the provided array is too small. Co-authored-by: Bastien Montagne <bastien@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/115967
This commit is contained in:
committed by
Bastien Montagne
parent
2f8fb76313
commit
dc8e2c09d9
@@ -317,6 +317,69 @@ class TestPropArrayDynamicArg(unittest.TestCase):
|
||||
self.assertEqual(tuple([1.0] * self.dims), tuple([vg.weight(i) for i in range(self.dims)]))
|
||||
|
||||
|
||||
class TestPropArrayInvalidForeachGetSet(unittest.TestCase):
|
||||
"""
|
||||
Test proper detection of invalid usages of foreach_get/foreach_set.
|
||||
"""
|
||||
|
||||
dims = 8
|
||||
|
||||
def setUp(self):
|
||||
self.me = bpy.data.meshes.new("")
|
||||
self.me.vertices.add(self.dims)
|
||||
self.ob = bpy.data.objects.new("", self.me)
|
||||
|
||||
def tearDown(self):
|
||||
bpy.data.objects.remove(self.ob)
|
||||
bpy.data.meshes.remove(self.me)
|
||||
self.me = None
|
||||
self.ob = None
|
||||
|
||||
def test_foreach_valid(self):
|
||||
me = self.me
|
||||
|
||||
# Non-array (scalar) data access.
|
||||
valid_1b_list = [False] * len(me.vertices)
|
||||
me.vertices.foreach_get("select", valid_1b_list)
|
||||
self.assertEqual(tuple([True] * self.dims), tuple(valid_1b_list))
|
||||
|
||||
valid_1b_list = [False] * len(me.vertices)
|
||||
me.vertices.foreach_set("select", valid_1b_list)
|
||||
for v in me.vertices:
|
||||
self.assertFalse(v.select)
|
||||
|
||||
# Array (vector) data access.
|
||||
valid_3f_list = [1.0] * (len(me.vertices) * 3)
|
||||
me.vertices.foreach_get("co", valid_3f_list)
|
||||
self.assertEqual(tuple([0.0] * self.dims * 3), tuple(valid_3f_list))
|
||||
|
||||
valid_3f_list = [1.0] * (len(me.vertices) * 3)
|
||||
me.vertices.foreach_set("co", valid_3f_list)
|
||||
for v in me.vertices:
|
||||
self.assertEqual(tuple(v.co), (1.0, 1.0, 1.0))
|
||||
|
||||
def test_foreach_invalid_smaller_array(self):
|
||||
me = self.me
|
||||
|
||||
# Non-array (scalar) data access.
|
||||
invalid_1b_list = [False] * (len(me.vertices) - 1)
|
||||
with self.assertRaises(RuntimeError):
|
||||
me.vertices.foreach_get("select", invalid_1b_list)
|
||||
|
||||
invalid_1b_list = [False] * (len(me.vertices) - 1)
|
||||
with self.assertRaises(RuntimeError):
|
||||
me.vertices.foreach_set("select", invalid_1b_list)
|
||||
|
||||
# Array (vector) data access.
|
||||
invalid_3f_list = [1.0] * (len(me.vertices) * 3 - 1)
|
||||
with self.assertRaises(RuntimeError):
|
||||
me.vertices.foreach_get("co", invalid_3f_list)
|
||||
|
||||
invalid_3f_list = [1.0] * (len(me.vertices) * 3 - 1)
|
||||
with self.assertRaises(RuntimeError):
|
||||
me.vertices.foreach_set("co", invalid_3f_list)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
|
||||
|
||||
Reference in New Issue
Block a user