Skip to content

Commit 50bd33b

Browse files
Matt Wozniskiprojectgus
authored andcommitted
py/binary,objint: Add overflow checks for int to bytes conversions.
For both small and long integers, raise an exception if calling struct.pack, adding an element to an array.array, or formatting an int with int.to_bytes would overflow the requested size. (Cherry-picked from CircuitPython commit 095c844.) Signed-off-by: Angus Gratton <angus@redyak.com.au>
1 parent 78d017f commit 50bd33b

File tree

3 files changed

+61
-4
lines changed

3 files changed

+61
-4
lines changed

py/binary.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,15 +441,18 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p
441441
break;
442442
}
443443
#endif
444-
default:
444+
default: {
445+
bool signed_type = is_signed(val_type);
445446
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
446447
if (mp_obj_is_exact_type(val_in, &mp_type_int)) {
448+
mp_obj_int_buffer_overflow_check(val_in, size, signed_type);
447449
mp_obj_int_to_bytes_impl(val_in, struct_type == '>', size, p);
448450
return;
449451
}
450452
#endif
451453

452454
val = mp_obj_get_int(val_in);
455+
mp_obj_int_buffer_overflow_check(val_in, size, signed_type);
453456
// zero/sign extend if needed
454457
if (MP_BYTES_PER_OBJ_WORD < 8 && size > sizeof(val)) {
455458
int c = (mp_int_t)val < 0 ? 0xff : 0x00;
@@ -459,6 +462,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p
459462
}
460463
}
461464
break;
465+
}
462466
}
463467

464468
mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val);
@@ -478,16 +482,24 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_
478482
case 'O':
479483
((mp_obj_t *)p)[index] = val_in;
480484
break;
481-
default:
485+
default: {
486+
size_t size = mp_binary_get_size('@', typecode, NULL);
487+
bool signed_type = is_signed(typecode);
482488
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
483489
if (mp_obj_is_exact_type(val_in, &mp_type_int)) {
484-
size_t size = mp_binary_get_size('@', typecode, NULL);
490+
mp_obj_int_buffer_overflow_check(val_in, size, signed_type);
485491
mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG,
486492
size, (uint8_t *)p + index * size);
487493
return;
488494
}
489495
#endif
490-
mp_binary_set_val_array_from_int(typecode, p, index, mp_obj_get_int(val_in));
496+
mp_int_t val = mp_obj_get_int(val_in);
497+
if (val < 0 && typecode == BYTEARRAY_TYPECODE) {
498+
val = val & 0xFF;
499+
}
500+
mp_obj_int_buffer_overflow_check(mp_obj_new_int(val), size, signed_type);
501+
mp_binary_set_val_array_from_int(typecode, p, index, val);
502+
}
491503
}
492504
}
493505

py/objint.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,48 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co
300300
return b;
301301
}
302302

303+
void mp_obj_int_buffer_overflow_check(mp_obj_t self_in, size_t nbytes, bool is_signed) {
304+
if (is_signed) {
305+
// edge = 1 << (nbytes * 8 - 1)
306+
mp_obj_t edge = mp_binary_op(MP_BINARY_OP_INPLACE_LSHIFT,
307+
mp_obj_new_int(1),
308+
mp_obj_new_int(nbytes * 8 - 1));
309+
310+
// if self >= edge, we don't fit
311+
if (mp_binary_op(MP_BINARY_OP_MORE_EQUAL, self_in, edge) == mp_const_true) {
312+
goto raise;
313+
}
314+
315+
// edge = -edge
316+
edge = mp_unary_op(MP_UNARY_OP_NEGATIVE, edge);
317+
318+
// if self < edge, we don't fit
319+
if (mp_binary_op(MP_BINARY_OP_LESS, self_in, edge) == mp_const_true) {
320+
goto raise;
321+
}
322+
} else {
323+
if (mp_obj_int_sign(self_in) < 0) {
324+
// Negative numbers never fit in an unsigned value
325+
goto raise;
326+
}
327+
328+
// edge = 1 << (nbytes * 8)
329+
mp_obj_t edge = mp_binary_op(MP_BINARY_OP_INPLACE_LSHIFT,
330+
mp_obj_new_int(1),
331+
mp_obj_new_int(nbytes * 8));
332+
333+
// if self >= edge, we don't fit
334+
if (mp_binary_op(MP_BINARY_OP_MORE_EQUAL, self_in, edge) == mp_const_true) {
335+
goto raise;
336+
}
337+
}
338+
339+
return;
340+
341+
raise:
342+
mp_raise_msg_varg(&mp_type_OverflowError, MP_ERROR_TEXT("value would overflow a %d byte buffer"), nbytes);
343+
}
344+
303345
#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE
304346

305347
int mp_obj_int_sign(mp_obj_t self_in) {
@@ -434,6 +476,8 @@ static mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) {
434476
vstr_init_len(&vstr, dlen);
435477
byte *data = (byte *)vstr.buf;
436478

479+
mp_obj_int_buffer_overflow_check(args[0], dlen, false);
480+
437481
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
438482
if (!mp_obj_is_small_int(args[0])) {
439483
overflow = !mp_obj_int_to_bytes_impl(args[0], big_endian, dlen, data);

py/objint.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co
5353
int base, const char *prefix, char base_char, char comma);
5454
char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in,
5555
int base, const char *prefix, char base_char, char comma);
56+
void mp_obj_int_buffer_overflow_check(mp_obj_t self_in, size_t nbytes, bool is_signed);
5657
mp_int_t mp_obj_int_hash(mp_obj_t self_in);
5758
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf);
5859
// Returns true if 'self_in' fit into 'len' bytes of 'buf' without overflowing, 'buf' is truncated otherwise.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy