extmod/moductypes: Validate the descriptor tuple.

Fixes various null dereferencing, out-of-bounds memory accesses and
`assert(0)` failures in the case of an invalid `uctypes` descriptor.

By design `uctypes` can crash because it accesses arbitrary memory, but at
least describing the descriptor layout should be forced to be correct and
not crash.

Fixes issue #12702.

Signed-off-by: stijn <stijn@ignitron.net>
This commit is contained in:
stijn 2023-11-08 13:43:28 +01:00 committed by Damien George
parent 6db91dfefb
commit 444d7bacbe
5 changed files with 63 additions and 10 deletions

View File

@ -143,6 +143,10 @@ static inline mp_uint_t uctypes_struct_scalar_size(int val_type) {
// Get size of aggregate type descriptor // Get size of aggregate type descriptor
static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_uint_t *max_field_size) { static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_uint_t *max_field_size) {
if (t->len == 0) {
syntax_error();
}
mp_uint_t total_size = 0; mp_uint_t total_size = 0;
mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]); mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
@ -150,8 +154,15 @@ static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_
switch (agg_type) { switch (agg_type) {
case STRUCT: case STRUCT:
if (t->len != 2) {
syntax_error();
}
return uctypes_struct_size(t->items[1], layout_type, max_field_size); return uctypes_struct_size(t->items[1], layout_type, max_field_size);
case PTR: case PTR:
// Second field ignored, but should still be present for consistency.
if (t->len != 2) {
syntax_error();
}
if (sizeof(void *) > *max_field_size) { if (sizeof(void *) > *max_field_size) {
*max_field_size = sizeof(void *); *max_field_size = sizeof(void *);
} }
@ -167,15 +178,17 @@ static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_
if (item_s > *max_field_size) { if (item_s > *max_field_size) {
*max_field_size = item_s; *max_field_size = item_s;
} }
} else { } else if (t->len == 3) {
// Elements of array are aggregates // Elements of array are aggregates
item_s = uctypes_struct_size(t->items[2], layout_type, max_field_size); item_s = uctypes_struct_size(t->items[2], layout_type, max_field_size);
} else {
syntax_error();
} }
return item_s * arr_sz; return item_s * arr_sz;
} }
default: default:
assert(0); syntax_error();
} }
return total_size; return total_size;

View File

@ -43,8 +43,47 @@ assert uctypes.sizeof(S.arr4) == 6
print(uctypes.sizeof(S.sub)) print(uctypes.sizeof(S.sub))
assert uctypes.sizeof(S.sub) == 1 assert uctypes.sizeof(S.sub) == 1
# invalid descriptor # invalid descriptors
try: try:
print(uctypes.sizeof([])) print(uctypes.sizeof([]))
except TypeError: except TypeError:
print("TypeError") print("TypeError")
try:
print(uctypes.sizeof(()))
except TypeError:
print("TypeError")
try:
print(uctypes.sizeof(("garbage",)))
except TypeError:
print("TypeError")
try:
# PTR * 3 is intended to be an invalid agg_type (STRUCT, PTR, ARRAY are valid ones).
print(uctypes.sizeof((uctypes.PTR * 3,)))
except TypeError:
print("TypeError")
try:
print(uctypes.sizeof((0, {}, "garbage")))
except TypeError:
print("TypeError")
try:
print(uctypes.sizeof((uctypes.PTR | 0,)))
except TypeError:
print("TypeError")
try:
print(uctypes.sizeof((uctypes.ARRAY | 0,)))
except TypeError:
print("TypeError")
try:
print(uctypes.sizeof((uctypes.ARRAY | 0, 1, {}, "garbage")))
except TypeError:
print("TypeError")
# empty descriptor
print(uctypes.sizeof({}))

View File

@ -5,3 +5,11 @@ TypeError
6 6
1 1
TypeError TypeError
TypeError
TypeError
TypeError
TypeError
TypeError
TypeError
TypeError
0

View File

@ -45,9 +45,3 @@ assert uctypes.sizeof(S.arr4) == 6
print(uctypes.sizeof(S.sub)) print(uctypes.sizeof(S.sub))
assert uctypes.sizeof(S.sub) == 1 assert uctypes.sizeof(S.sub) == 1
# invalid descriptor
try:
print(uctypes.sizeof([]))
except TypeError:
print("TypeError")

View File

@ -4,4 +4,3 @@
TypeError TypeError
6 6
1 1
TypeError