8000 heap-buffer-overflow: from 0 length int_to_bytes · Issue #13041 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content
heap-buffer-overflow: from 0 length int_to_bytes #13041
Closed
@junwha

Description

@junwha

Thank you for the super fast responses for the previous bugs, we found new bug in the core module.

Summary

  • OS: Ubuntu 22.04
  • version: micropython a00c9d5
  • port: unix
  • contribution: Junwha Hong and Wonil Jang @S2-Lab, UNIST
  • description: Because the length check of int_to_bytes does not include 0, it leads to heap-buffer-overflow and Segmentation fault

PoC

b = bytes(range(20))
ib = int.from_bytes(b, "big")
print(ib.to_bytes( 0, "big"))

Expected behavior in python3

python3 ./poc_to_bytes
Traceback (most recent call last):
  File "/home/qbit/testing-2023/micropython/fuzzing/output/default/crashes/./poc_to_bytes", line 3, in <module>
    print(ib.to_bytes( 0, "big"))
OverflowError: int too big to convert

micropython

Segmentation fault

Problem Statement

For the PoC, at py/objint.c:423, the len is 0, thus it pass through if (len < 0)

STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) {
    // TODO: Support signed param (assumes signed=False)
    (void)n_args;

    mp_int_t len = mp_obj_get_int(args[1]);
    if (len < 0) {
        mp_raise_ValueError(NULL);
    }
    bool big_endian = args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little);

    vstr_t vstr;
    vstr_init_len(&vstr, len);
    byte *data = (byte *)vstr.buf;
    memset(data, 0, len);

    #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
    if (!mp_obj_is_small_int(args[0])) {
        mp_obj_int_to_bytes_impl(args[0], big_endian, len, data);

Then, it calls mpz_as_bytes at with zero len. here, thus b and buf are same before the for loop.

void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf) {
    byte *b = buf;
    if (big_endian) {
        b += len;
    }
    mpz_dig_t *zdig = z->dig;
    int bits = 0;
    mpz_dbl_dig_t d = 0;
    mpz_dbl_dig_t carry = 1;
    for (size_t zlen = z->len; zlen > 0; --zlen) {
        bits += DIG_SIZE;
        d = (d << DIG_SIZE) | *zdig++;
        for (; bits >= 8; bits -= 8, d >>= 8) {
            mpz_dig_t val = d;
            if (z->neg) {
                val = (~val & 0xff) + carry;
                carry = val >> 8;
            }
            if (big_endian) {
                *--b = val;
                if (b == buf) {
                    return;
                }
            } else {
                ...
            }
        }
    }

    // fill remainder of buf with zero/sign extension of the integer
    if (big_endian) {
        len = b - buf;
    } else {
        ...
    }
    memset(buf, z->neg ? 0xff : 0x00, len);

Because it never satisfy the condition if (b == buf), the b overflows to the left object. (under the start of the pointer).

Additionally, the length is -20, because unexpectedly the condition b < buf was satisfied.

Patch

Patch is very simple. we just need to include 0 to the case.

STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) {
    // TODO: Support signed param (assumes signed=False)
    (void)n_args;

    mp_int_t len = mp_obj_get_int(args[1]);
    if (len <= 0) {
        mp_raise_ValueError(NULL);
    }
    bool big_endian = args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little);

    vstr_t vstr;
    vstr_init_len(&vstr, len);
    byte *data = (byte *)vstr.buf;
    memset(data, 0, len);

    #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
    if (!mp_obj_is_small_int(args[0])) {
        **mp_obj_int_to_bytes_impl(args[0], big_endian, len, data);**

Crash log

#0 0x5555556ae6e0 in mpz_as_bytes /home/qbit/testing-2023/micropython/ports/unix/../../py/mpz.c:1611:22
#1 0x55555574583a in int_to_bytes /home/qbit/testing-2023/micropython/ports/unix/../../py/objint.c:440:9
#2 0x555555782c1c in mp_execute_bytecode /home/qbit/testing-2023/micropython/ports/unix/../../py/vm.c:1042:21
#3 0x55555574261b in fun_bc_call /home/qbit/testing-2023/micropython/ports/unix/../../py/objfun.c:273:42
#4 0x555555903f3d in execute_from_lexer /home/qbit/testing-2023/micropython/ports/unix/main.c:161:13
****#5 0x555555902ad5 in do_file /home/qbit/testing-2023/micropython/ports/unix/main.c:310:12
#6 0x555555902ad5 in main_ /home/qbit/testing-2023/micropython/ports/unix/main.c:722:19
#7 0x7ffff7c29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#8 0x7ffff7c29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#9 0x555555593a34 in _start (/home/qbit/testing-2023/micropython/ports/unix/build-standard/micropython+0x3fa34)

Thank you for taking the time to review our bug report! :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugpy-coreRelates to py/ directory in source

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0