gh-111178: fix UBSan failures in Python/hamt.c (GH-128247)

* fix UBSan failures for `PyHamtObject`
* fix UBSan failures for `PyHamtNode_Array`
* fix UBSan failures for `PyHamtNode_Collision`
* fix UBSan failures for `PyHamtNode_Bitmap`
This commit is contained in:
Bénédikt Tran 2025-01-08 14:50:40 +01:00 committed by GitHub
parent 74a517181a
commit c22302ecea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -319,6 +319,8 @@ typedef struct {
Py_ssize_t a_count; Py_ssize_t a_count;
} PyHamtNode_Array; } PyHamtNode_Array;
#define _PyHamtNode_Array_CAST(op) ((PyHamtNode_Array *)(op))
typedef struct { typedef struct {
PyObject_VAR_HEAD PyObject_VAR_HEAD
@ -326,6 +328,8 @@ typedef struct {
PyObject *c_array[1]; PyObject *c_array[1];
} PyHamtNode_Collision; } PyHamtNode_Collision;
#define _PyHamtNode_Collision_CAST(op) ((PyHamtNode_Collision *)(op))
static PyHamtObject * static PyHamtObject *
hamt_alloc(void); hamt_alloc(void);
@ -479,6 +483,8 @@ error:
#endif /* Py_DEBUG */ #endif /* Py_DEBUG */
/////////////////////////////////// Bitmap Node /////////////////////////////////// Bitmap Node
#define _PyHamtNode_Bitmap_CAST(op) ((PyHamtNode_Bitmap *)(op))
static PyHamtNode * static PyHamtNode *
hamt_node_bitmap_new(Py_ssize_t size) hamt_node_bitmap_new(Py_ssize_t size)
@ -1083,30 +1089,27 @@ hamt_node_bitmap_find(PyHamtNode_Bitmap *self,
} }
static int static int
hamt_node_bitmap_traverse(PyHamtNode_Bitmap *self, visitproc visit, void *arg) hamt_node_bitmap_traverse(PyObject *op, visitproc visit, void *arg)
{ {
/* Bitmap's tp_traverse */ /* Bitmap's tp_traverse */
PyHamtNode_Bitmap *self = _PyHamtNode_Bitmap_CAST(op);
Py_ssize_t i; for (Py_ssize_t i = Py_SIZE(self); --i >= 0;) {
for (i = Py_SIZE(self); --i >= 0; ) {
Py_VISIT(self->b_array[i]); Py_VISIT(self->b_array[i]);
} }
return 0; return 0;
} }
static void static void
hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self) hamt_node_bitmap_dealloc(PyObject *self)
{ {
/* Bitmap's tp_dealloc */ /* Bitmap's tp_dealloc */
Py_ssize_t len = Py_SIZE(self); PyHamtNode_Bitmap *node = _PyHamtNode_Bitmap_CAST(self);
Py_ssize_t i; Py_ssize_t i, len = Py_SIZE(self);
if (Py_SIZE(self) == 0) { if (len == 0) {
/* The empty node is statically allocated. */ /* The empty node is statically allocated. */
assert(self == &_Py_SINGLETON(hamt_bitmap_node_empty)); assert(node == &_Py_SINGLETON(hamt_bitmap_node_empty));
#ifdef Py_DEBUG #ifdef Py_DEBUG
_Py_FatalRefcountError("deallocating the empty hamt node bitmap singleton"); _Py_FatalRefcountError("deallocating the empty hamt node bitmap singleton");
#else #else
@ -1120,11 +1123,11 @@ hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self)
if (len > 0) { if (len > 0) {
i = len; i = len;
while (--i >= 0) { while (--i >= 0) {
Py_XDECREF(self->b_array[i]); Py_XDECREF(node->b_array[i]);
} }
} }
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free(self);
Py_TRASHCAN_END Py_TRASHCAN_END
} }
@ -1489,38 +1492,30 @@ hamt_node_collision_find(PyHamtNode_Collision *self,
static int static int
hamt_node_collision_traverse(PyHamtNode_Collision *self, hamt_node_collision_traverse(PyObject *op, visitproc visit, void *arg)
visitproc visit, void *arg)
{ {
/* Collision's tp_traverse */ /* Collision's tp_traverse */
PyHamtNode_Collision *self = _PyHamtNode_Collision_CAST(op);
Py_ssize_t i; for (Py_ssize_t i = Py_SIZE(self); --i >= 0; ) {
for (i = Py_SIZE(self); --i >= 0; ) {
Py_VISIT(self->c_array[i]); Py_VISIT(self->c_array[i]);
} }
return 0; return 0;
} }
static void static void
hamt_node_collision_dealloc(PyHamtNode_Collision *self) hamt_node_collision_dealloc(PyObject *self)
{ {
/* Collision's tp_dealloc */ /* Collision's tp_dealloc */
Py_ssize_t len = Py_SIZE(self); Py_ssize_t len = Py_SIZE(self);
PyObject_GC_UnTrack(self); PyObject_GC_UnTrack(self);
Py_TRASHCAN_BEGIN(self, hamt_node_collision_dealloc) Py_TRASHCAN_BEGIN(self, hamt_node_collision_dealloc)
if (len > 0) { if (len > 0) {
PyHamtNode_Collision *node = _PyHamtNode_Collision_CAST(self);
while (--len >= 0) { while (--len >= 0) {
Py_XDECREF(self->c_array[len]); Py_XDECREF(node->c_array[len]);
} }
} }
Py_TYPE(self)->tp_free(self);
Py_TYPE(self)->tp_free((PyObject *)self);
Py_TRASHCAN_END Py_TRASHCAN_END
} }
@ -1868,35 +1863,27 @@ hamt_node_array_find(PyHamtNode_Array *self,
} }
static int static int
hamt_node_array_traverse(PyHamtNode_Array *self, hamt_node_array_traverse(PyObject *op, visitproc visit, void *arg)
visitproc visit, void *arg)
{ {
/* Array's tp_traverse */ /* Array's tp_traverse */
PyHamtNode_Array *self = _PyHamtNode_Array_CAST(op);
Py_ssize_t i; for (Py_ssize_t i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
Py_VISIT(self->a_array[i]); Py_VISIT(self->a_array[i]);
} }
return 0; return 0;
} }
static void static void
hamt_node_array_dealloc(PyHamtNode_Array *self) hamt_node_array_dealloc(PyObject *self)
{ {
/* Array's tp_dealloc */ /* Array's tp_dealloc */
Py_ssize_t i;
PyObject_GC_UnTrack(self); PyObject_GC_UnTrack(self);
Py_TRASHCAN_BEGIN(self, hamt_node_array_dealloc) Py_TRASHCAN_BEGIN(self, hamt_node_array_dealloc)
PyHamtNode_Array *obj = _PyHamtNode_Array_CAST(self);
for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) { for (Py_ssize_t i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
Py_XDECREF(self->a_array[i]); Py_XDECREF(obj->a_array[i]);
} }
Py_TYPE(self)->tp_free(self);
Py_TYPE(self)->tp_free((PyObject *)self);
Py_TRASHCAN_END Py_TRASHCAN_END
} }
@ -2605,6 +2592,8 @@ static PyObject *
hamt_dump(PyHamtObject *self); hamt_dump(PyHamtObject *self);
#endif #endif
#define _PyHamtObject_CAST(op) ((PyHamtObject *)(op))
static PyObject * static PyObject *
hamt_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) hamt_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
@ -2613,24 +2602,27 @@ hamt_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
} }
static int static int
hamt_tp_clear(PyHamtObject *self) hamt_tp_clear(PyObject *op)
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
Py_CLEAR(self->h_root); Py_CLEAR(self->h_root);
return 0; return 0;
} }
static int static int
hamt_tp_traverse(PyHamtObject *self, visitproc visit, void *arg) hamt_tp_traverse(PyObject *op, visitproc visit, void *arg)
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
Py_VISIT(self->h_root); Py_VISIT(self->h_root);
return 0; return 0;
} }
static void static void
hamt_tp_dealloc(PyHamtObject *self) hamt_tp_dealloc(PyObject *self)
{ {
if (self == _empty_hamt) { PyHamtObject *obj = _PyHamtObject_CAST(self);
if (obj == _empty_hamt) {
/* The empty one is statically allocated. */ /* The empty one is statically allocated. */
#ifdef Py_DEBUG #ifdef Py_DEBUG
_Py_FatalRefcountError("deallocating the empty hamt singleton"); _Py_FatalRefcountError("deallocating the empty hamt singleton");
@ -2640,8 +2632,8 @@ hamt_tp_dealloc(PyHamtObject *self)
} }
PyObject_GC_UnTrack(self); PyObject_GC_UnTrack(self);
if (self->h_weakreflist != NULL) { if (obj->h_weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject*)self); PyObject_ClearWeakRefs(self);
} }
(void)hamt_tp_clear(self); (void)hamt_tp_clear(self);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
@ -2673,16 +2665,18 @@ hamt_tp_richcompare(PyObject *v, PyObject *w, int op)
} }
static int static int
hamt_tp_contains(PyHamtObject *self, PyObject *key) hamt_tp_contains(PyObject *op, PyObject *key)
{ {
PyObject *val; PyObject *val;
PyHamtObject *self = _PyHamtObject_CAST(op);
return _PyHamt_Find(self, key, &val); return _PyHamt_Find(self, key, &val);
} }
static PyObject * static PyObject *
hamt_tp_subscript(PyHamtObject *self, PyObject *key) hamt_tp_subscript(PyObject *op, PyObject *key)
{ {
PyObject *val; PyObject *val;
PyHamtObject *self = _PyHamtObject_CAST(op);
hamt_find_t res = hamt_find(self, key, &val); hamt_find_t res = hamt_find(self, key, &val);
switch (res) { switch (res) {
case F_ERROR: case F_ERROR:
@ -2698,19 +2692,21 @@ hamt_tp_subscript(PyHamtObject *self, PyObject *key)
} }
static Py_ssize_t static Py_ssize_t
hamt_tp_len(PyHamtObject *self) hamt_tp_len(PyObject *op)
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
return _PyHamt_Len(self); return _PyHamt_Len(self);
} }
static PyObject * static PyObject *
hamt_tp_iter(PyHamtObject *self) hamt_tp_iter(PyObject *op)
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
return _PyHamt_NewIterKeys(self); return _PyHamt_NewIterKeys(self);
} }
static PyObject * static PyObject *
hamt_py_set(PyHamtObject *self, PyObject *args) hamt_py_set(PyObject *op, PyObject *args)
{ {
PyObject *key; PyObject *key;
PyObject *val; PyObject *val;
@ -2719,11 +2715,12 @@ hamt_py_set(PyHamtObject *self, PyObject *args)
return NULL; return NULL;
} }
PyHamtObject *self = _PyHamtObject_CAST(op);
return (PyObject *)_PyHamt_Assoc(self, key, val); return (PyObject *)_PyHamt_Assoc(self, key, val);
} }
static PyObject * static PyObject *
hamt_py_get(PyHamtObject *self, PyObject *args) hamt_py_get(PyObject *op, PyObject *args)
{ {
PyObject *key; PyObject *key;
PyObject *def = NULL; PyObject *def = NULL;
@ -2733,6 +2730,7 @@ hamt_py_get(PyHamtObject *self, PyObject *args)
} }
PyObject *val = NULL; PyObject *val = NULL;
PyHamtObject *self = _PyHamtObject_CAST(op);
hamt_find_t res = hamt_find(self, key, &val); hamt_find_t res = hamt_find(self, key, &val);
switch (res) { switch (res) {
case F_ERROR: case F_ERROR:
@ -2750,67 +2748,63 @@ hamt_py_get(PyHamtObject *self, PyObject *args)
} }
static PyObject * static PyObject *
hamt_py_delete(PyHamtObject *self, PyObject *key) hamt_py_delete(PyObject *op, PyObject *key)
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
return (PyObject *)_PyHamt_Without(self, key); return (PyObject *)_PyHamt_Without(self, key);
} }
static PyObject * static PyObject *
hamt_py_items(PyHamtObject *self, PyObject *args) hamt_py_items(PyObject *op, PyObject *args)
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
return _PyHamt_NewIterItems(self); return _PyHamt_NewIterItems(self);
} }
static PyObject * static PyObject *
hamt_py_values(PyHamtObject *self, PyObject *args) hamt_py_values(PyObject *op, PyObject *args)
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
return _PyHamt_NewIterValues(self); return _PyHamt_NewIterValues(self);
} }
static PyObject * static PyObject *
hamt_py_keys(PyHamtObject *self, PyObject *Py_UNUSED(args)) hamt_py_keys(PyObject *op, PyObject *Py_UNUSED(args))
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
return _PyHamt_NewIterKeys(self); return _PyHamt_NewIterKeys(self);
} }
#ifdef Py_DEBUG #ifdef Py_DEBUG
static PyObject * static PyObject *
hamt_py_dump(PyHamtObject *self, PyObject *Py_UNUSED(args)) hamt_py_dump(PyObject *op, PyObject *Py_UNUSED(args))
{ {
PyHamtObject *self = _PyHamtObject_CAST(op);
return hamt_dump(self); return hamt_dump(self);
} }
#endif #endif
static PyMethodDef PyHamt_methods[] = { static PyMethodDef PyHamt_methods[] = {
{"set", _PyCFunction_CAST(hamt_py_set), METH_VARARGS, NULL}, {"set", hamt_py_set, METH_VARARGS, NULL},
{"get", _PyCFunction_CAST(hamt_py_get), METH_VARARGS, NULL}, {"get", hamt_py_get, METH_VARARGS, NULL},
{"delete", _PyCFunction_CAST(hamt_py_delete), METH_O, NULL}, {"delete", hamt_py_delete, METH_O, NULL},
{"items", _PyCFunction_CAST(hamt_py_items), METH_NOARGS, NULL}, {"items", hamt_py_items, METH_NOARGS, NULL},
{"keys", _PyCFunction_CAST(hamt_py_keys), METH_NOARGS, NULL}, {"keys", hamt_py_keys, METH_NOARGS, NULL},
{"values", _PyCFunction_CAST(hamt_py_values), METH_NOARGS, NULL}, {"values", hamt_py_values, METH_NOARGS, NULL},
#ifdef Py_DEBUG #ifdef Py_DEBUG
{"__dump__", _PyCFunction_CAST(hamt_py_dump), METH_NOARGS, NULL}, {"__dump__", hamt_py_dump, METH_NOARGS, NULL},
#endif #endif
{NULL, NULL} {NULL, NULL}
}; };
static PySequenceMethods PyHamt_as_sequence = { static PySequenceMethods PyHamt_as_sequence = {
0, /* sq_length */ .sq_contains = hamt_tp_contains,
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)hamt_tp_contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
}; };
static PyMappingMethods PyHamt_as_mapping = { static PyMappingMethods PyHamt_as_mapping = {
(lenfunc)hamt_tp_len, /* mp_length */ .mp_length = hamt_tp_len,
(binaryfunc)hamt_tp_subscript, /* mp_subscript */ .mp_subscript = hamt_tp_subscript,
}; };
PyTypeObject _PyHamt_Type = { PyTypeObject _PyHamt_Type = {
@ -2820,13 +2814,13 @@ PyTypeObject _PyHamt_Type = {
.tp_methods = PyHamt_methods, .tp_methods = PyHamt_methods,
.tp_as_mapping = &PyHamt_as_mapping, .tp_as_mapping = &PyHamt_as_mapping,
.tp_as_sequence = &PyHamt_as_sequence, .tp_as_sequence = &PyHamt_as_sequence,
.tp_iter = (getiterfunc)hamt_tp_iter, .tp_iter = hamt_tp_iter,
.tp_dealloc = (destructor)hamt_tp_dealloc, .tp_dealloc = hamt_tp_dealloc,
.tp_getattro = PyObject_GenericGetAttr, .tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_richcompare = hamt_tp_richcompare, .tp_richcompare = hamt_tp_richcompare,
.tp_traverse = (traverseproc)hamt_tp_traverse, .tp_traverse = hamt_tp_traverse,
.tp_clear = (inquiry)hamt_tp_clear, .tp_clear = hamt_tp_clear,
.tp_new = hamt_tp_new, .tp_new = hamt_tp_new,
.tp_weaklistoffset = offsetof(PyHamtObject, h_weakreflist), .tp_weaklistoffset = offsetof(PyHamtObject, h_weakreflist),
.tp_hash = PyObject_HashNotImplemented, .tp_hash = PyObject_HashNotImplemented,
@ -2841,10 +2835,10 @@ PyTypeObject _PyHamt_ArrayNode_Type = {
"hamt_array_node", "hamt_array_node",
sizeof(PyHamtNode_Array), sizeof(PyHamtNode_Array),
0, 0,
.tp_dealloc = (destructor)hamt_node_array_dealloc, .tp_dealloc = hamt_node_array_dealloc,
.tp_getattro = PyObject_GenericGetAttr, .tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)hamt_node_array_traverse, .tp_traverse = hamt_node_array_traverse,
.tp_free = PyObject_GC_Del, .tp_free = PyObject_GC_Del,
.tp_hash = PyObject_HashNotImplemented, .tp_hash = PyObject_HashNotImplemented,
}; };
@ -2854,10 +2848,10 @@ PyTypeObject _PyHamt_BitmapNode_Type = {
"hamt_bitmap_node", "hamt_bitmap_node",
sizeof(PyHamtNode_Bitmap) - sizeof(PyObject *), sizeof(PyHamtNode_Bitmap) - sizeof(PyObject *),
sizeof(PyObject *), sizeof(PyObject *),
.tp_dealloc = (destructor)hamt_node_bitmap_dealloc, .tp_dealloc = hamt_node_bitmap_dealloc,
.tp_getattro = PyObject_GenericGetAttr, .tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)hamt_node_bitmap_traverse, .tp_traverse = hamt_node_bitmap_traverse,
.tp_free = PyObject_GC_Del, .tp_free = PyObject_GC_Del,
.tp_hash = PyObject_HashNotImplemented, .tp_hash = PyObject_HashNotImplemented,
}; };
@ -2867,10 +2861,10 @@ PyTypeObject _PyHamt_CollisionNode_Type = {
"hamt_collision_node", "hamt_collision_node",
sizeof(PyHamtNode_Collision) - sizeof(PyObject *), sizeof(PyHamtNode_Collision) - sizeof(PyObject *),
sizeof(PyObject *), sizeof(PyObject *),
.tp_dealloc = (destructor)hamt_node_collision_dealloc, .tp_dealloc = hamt_node_collision_dealloc,
.tp_getattro = PyObject_GenericGetAttr, .tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)hamt_node_collision_traverse, .tp_traverse = hamt_node_collision_traverse,
.tp_free = PyObject_GC_Del, .tp_free = PyObject_GC_Del,
.tp_hash = PyObject_HashNotImplemented, .tp_hash = PyObject_HashNotImplemented,
}; };