Skip to content

Commit 7f1481c

Browse files
committed
py/misc.h: Add mp_checked_mul to replace CAPPED_SIZE_NUM_OBJ
1 parent b19d7fc commit 7f1481c

5 files changed

Lines changed: 76 additions & 18 deletions

File tree

py/misc.h

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,32 +67,52 @@ typedef unsigned int uint;
6767
#define MP_CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b))
6868
#define MP_ROUND_DIVIDE(a, b) (((a) + (b) / 2) / (b))
6969

70-
/** memory allocation ******************************************/
70+
#ifdef _MSC_VER
71+
static inline bool mp_mul_overflow(size_t a, size_t b, size_t *res) {
72+
// TODO what if size_t is uint64_t
73+
uint64_t ab = (uint64_t)a * (uint64_t)b;
74+
*res = ab;
75+
return ab > UINT32_MAX;
76+
}
77+
#else
78+
static inline bool mp_mul_overflow(size_t a, size_t b, size_t *res) {
79+
return __builtin_mul_overflow(a, b, res);
80+
}
81+
#endif
82+
83+
#if MICROPY_HEAP_OVERFLOW_CHECKS
84+
size_t mp_checked_mul(size_t a, size_t b);
85+
size_t mp_checked_add(size_t a, size_t b);
86+
#else
87+
static inline size_t mp_checked_mul(size_t a, size_t b) {
88+
return a * b;
89+
}
90+
static inline size_t mp_checked_add(size_t a, size_t b) {
91+
return a + b;
92+
}
93+
#endif
7194

7295

73-
// Ensure that m_new does not overflow the multiplication when computing sizeof(type) * num.
74-
#define CAPPED_SIZE_NUM_OBJ(type, num) ((sizeof(type) * (num)) / sizeof(type) != (size_t)(num) ? SIZE_MAX : sizeof(type) * (num))
75-
// Ensure that the multiplication and the addition do not overflow.
76-
#define CAPPED_SIZE_OBJ_NUM_VAR(obj_type, var_type, var_num) ((sizeof(var_type) * (var_num)) / sizeof(var_type) != (size_t)(var_num) || sizeof(obj_type) + sizeof(var_type) * (var_num) < sizeof(var_type) * (var_num) ? SIZE_MAX : sizeof(obj_type) + sizeof(var_type) * (var_num))
96+
/** memory allocation ******************************************/
7797

7898
// TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element)
7999

80-
#define m_new(type, num) ((type *)(m_malloc(CAPPED_SIZE_NUM_OBJ(type, num))))
81-
#define m_new_maybe(type, num) ((type *)(m_malloc_maybe(CAPPED_SIZE_NUM_OBJ(type, num))))
82-
#define m_new0(type, num) ((type *)(m_malloc0(CAPPED_SIZE_NUM_OBJ(type, num))))
100+
#define m_new(type, num) ((type *)(m_malloc(mp_checked_mul(sizeof(type), num))))
101+
#define m_new_maybe(type, num) ((type *)(m_malloc_maybe(mp_checked_mul(sizeof(type), num))))
102+
#define m_new0(type, num) ((type *)(m_malloc0(mp_checked_mul(sizeof(type), num))))
83103
#define m_new_obj(type) (m_new(type, 1))
84104
#define m_new_obj_maybe(type) (m_new_maybe(type, 1))
85-
#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc(CAPPED_SIZE_OBJ_NUM_VAR(offsetof(obj_type, var_field), var_type, var_num)))
86-
#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc0(CAPPED_SIZE_OBJ_NUM_VAR(offsetof(obj_type, var_field), var_type, var_num)))
87-
#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_maybe(CAPPED_SIZE_OBJ_NUM_VAR(offsetof(obj_type, var_field), var_type, var_num)))
105+
#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc(mp_checked_add(offsetof(obj_type, var_field), mp_checked_mul(sizeof(var_type), var_num))))
106+
#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc0(mp_checked_add(offsetof(obj_type, var_field), mp_checked_mul(sizeof(var_type), var_num))))
107+
#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_maybe(mp_checked_add(offsetof(obj_type, var_field), mp_checked_mul(sizeof(var_type), var_num))))
88108
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
89-
#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), CAPPED_SIZE_NUM_OBJ(type, old_num), CAPPED_SIZE_NUM_OBJ(type, new_num))))
90-
#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), CAPPED_SIZE_NUM_OBJ(type, old_num), CAPPED_SIZE_NUM_OBJ(type, new_num), (allow_move))))
91-
#define m_del(type, ptr, num) m_free(ptr, CAPPED_SIZE_NUM_OBJ(type, num))
92-
#define m_del_var(obj_type, var_field, var_type, var_num, ptr) (m_free(ptr, CAPPED_SIZE_OBJ_NUM_VAR(offsetof(obj_type, var_field), var_type, var_num)))
109+
#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * old_num, mp_checked_mul(sizeof(type), new_num))))
110+
#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * old_num, mp_checked_mul(sizeof(type), new_num), (allow_move))))
111+
#define m_del(type, ptr, num) m_free(ptr, sizeof(type) * num)
112+
#define m_del_var(obj_type, var_field, var_type, var_num, ptr) (m_free(ptr, offsetof(obj_type, var_field) + sizeof(var_type) * var_num))
93113
#else
94-
#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), CAPPED_SIZE_NUM_OBJ(type, new_num))))
95-
#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), CAPPED_SIZE_NUM_OBJ(type, new_num), (allow_move))))
114+
#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), mp_checked_mul(sizeof(type), new_num))))
115+
#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), mp_checked_mul(sizeof(type), new_num), (allow_move))))
96116
#define m_del(type, ptr, num) ((void)(num), m_free(ptr))
97117
#define m_del_var(obj_type, var_field, var_type, var_num, ptr) ((void)(var_num), m_free(ptr))
98118
#endif

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,11 @@
668668
#define MICROPY_TRACKED_ALLOC (0)
669669
#endif
670670

671+
// Whether to check if large sequences will overflow the pointer size
672+
#ifndef MICROPY_HEAP_OVERFLOW_CHECKS
673+
#define MICROPY_HEAP_OVERFLOW_CHECKS (1)
674+
#endif
675+
671676
// Whether to enable finalisers in the garbage collector (ie call __del__)
672677
#ifndef MICROPY_ENABLE_FINALISER
673678
#define MICROPY_ENABLE_FINALISER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)

py/obj.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,3 +595,21 @@ bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
595595
}
596596
return false;
597597
}
598+
599+
#if MICROPY_HEAP_OVERFLOW_CHECKS
600+
size_t mp_checked_mul(size_t a, size_t b) {
601+
size_t res;
602+
if (mp_mul_overflow(a, b, &res)) {
603+
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("multiplication overflow"));
604+
}
605+
return res;
606+
}
607+
608+
size_t mp_checked_add(size_t a, size_t b) {
609+
size_t res = a + b;
610+
if (res < a || res < b) {
611+
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("multiplication overflow"));
612+
}
613+
return res;
614+
}
615+
#endif

py/objstr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
367367
}
368368
}
369369
vstr_t vstr;
370-
vstr_init_len(&vstr, lhs_len * n);
370+
vstr_init_len(&vstr, mp_checked_mul(lhs_len, n));
371371
mp_seq_multiply(lhs_data, sizeof(*lhs_data), lhs_len, n, vstr.buf);
372372
return mp_obj_new_str_type_from_vstr(lhs_type, &vstr);
373373
}

tests/misc/heapalloc_overflow.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
try:
2+
s = "abcd" * 2**62
3+
# TODO 2**30 if size_t is 32-bit
4+
except OverflowError:
5+
pass
6+
except MemoryError:
7+
pass
8+
9+
try:
10+
s = [1, 2] * 2**62
11+
# TODO 2**30 if size_t is 32-bit
12+
except OverflowError:
13+
pass
14+
except MemoryError:
15+
pass

0 commit comments

Comments
 (0)