VYPR
Moderate severityNVD Advisory· Published Sep 17, 2024· Updated Sep 17, 2024

MicroPython objarray.c use after free

CVE-2024-8947

Description

MicroPython 1.22.2 contains a use-after-free vulnerability in py/objarray.c when a bytearray is resized and copied into itself, potentially allowing remote code execution.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

MicroPython 1.22.2 contains a use-after-free vulnerability in py/objarray.c when a bytearray is resized and copied into itself, potentially allowing remote code execution.

Vulnerability

Overview

CVE-2024-8947 is a use-after-free vulnerability in MicroPython version 1.22.2, specifically in the py/objarray.c file. The root cause lies in the array_subscr function, which handles slice assignments for bytearray objects. When a bytearray is resized and copied into itself (e.g., b[-1:] = b), the function stores a pointer to the source data before a potential reallocation. After realloc is called, the source pointer becomes dangling, leading to a use-after-free condition [2][3].

Exploitation

Details

The vulnerability can be triggered remotely by executing a crafted Python script that performs a self-assignment slice operation on a bytearray. The proof-of-concept provided by researchers is straightforward: b = bytearray(b'1234567'); for i in range(3): b[-1:] = b [3]. The attack complexity is considered high, and exploitation is difficult, but successful exploitation could allow an attacker to execute arbitrary code or cause a denial of service [2].

Impact

A successful exploit could lead to memory corruption, potentially enabling remote code execution in the context of the MicroPython interpreter. The vulnerability is rated as critical due to the possibility of remote exploitation and the severity of memory safety issues [2].

Mitigation

The issue has been addressed in MicroPython version 1.23.0, which includes the patch with commit identifier 4bed614e707c0644c06e117f848fa12605c711cd [1][2]. Users are strongly advised to upgrade to the latest version to mitigate the risk. No workarounds are available for affected versions.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
micropython-copyPyPI
<= 3.3.3.post3
micropython-ioPyPI
<= 0.1

Affected products

4

Patches

1
4bed614e707c

py/objarray: Fix use-after-free if extending a bytearray from itself.

https://github.com/micropython/micropythonAngus GrattonFeb 12, 2024via ghsa
5 files changed · +45 11
  • py/objarray.c+16 4 modified
    @@ -424,6 +424,13 @@ static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) {
         if (self->free < len) {
             self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz);
             self->free = 0;
    +
    +        if (self_in == arg_in) {
    +            // Get arg_bufinfo again in case self->items has moved
    +            //
    +            // (Note not possible to handle case that arg_in is a memoryview into self)
    +            mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
    +        }
         } else {
             self->free -= len;
         }
    @@ -456,7 +463,8 @@ static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value
                     #if MICROPY_PY_ARRAY_SLICE_ASSIGN
                     // Assign
                     size_t src_len;
    -                void *src_items;
    +                uint8_t *src_items;
    +                size_t src_offs = 0;
                     size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
                     if (mp_obj_is_obj(value) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type, subscr) == array_subscr) {
                         // value is array, bytearray or memoryview
    @@ -469,7 +477,7 @@ static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value
                         src_items = src_slice->items;
                         #if MICROPY_PY_BUILTINS_MEMORYVIEW
                         if (mp_obj_is_type(value, &mp_type_memoryview)) {
    -                        src_items = (uint8_t *)src_items + (src_slice->memview_offset * item_sz);
    +                        src_offs = src_slice->memview_offset * item_sz;
                         }
                         #endif
                     } else if (mp_obj_is_type(value, &mp_type_bytes)) {
    @@ -504,13 +512,17 @@ static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value
                             // TODO: alloc policy; at the moment we go conservative
                             o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz);
                             o->free = len_adj;
    +                        // m_renew may have moved o->items
    +                        if (src_items == dest_items) {
    +                            src_items = o->items;
    +                        }
                             dest_items = o->items;
                         }
                         mp_seq_replace_slice_grow_inplace(dest_items, o->len,
    -                        slice.start, slice.stop, src_items, src_len, len_adj, item_sz);
    +                        slice.start, slice.stop, src_items + src_offs, src_len, len_adj, item_sz);
                     } else {
                         mp_seq_replace_slice_no_grow(dest_items, o->len,
    -                        slice.start, slice.stop, src_items, src_len, item_sz);
    +                        slice.start, slice.stop, src_items + src_offs, src_len, item_sz);
                         // Clear "freed" elements at the end of list
                         // TODO: This is actually only needed for typecode=='O'
                         mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz);
    
  • tests/basics/bytearray_add.py+8 1 modified
    @@ -15,4 +15,11 @@
     
     # this inplace add tests the code when the buffer doesn't need to be increased
     b = bytearray()
    -b += b''
    +b += b""
    +
    +# extend a bytearray from itself
    +b = bytearray(b"abcdefgh")
    +for _ in range(4):
    +    c = bytearray(b)  # extra allocation, as above
    +    b.extend(b)
    +print(b)
    
  • tests/basics/bytearray_add_self.py+8 0 added
    @@ -0,0 +1,8 @@
    +# add a bytearray to itself
    +# This is not supported by CPython as of 3.11.18.
    +
    +b = bytearray(b"123456789")
    +for _ in range(4):
    +    c = bytearray(b)  # extra allocation increases chance 'b' has to relocate
    +    b += b
    +print(b)
    
  • tests/basics/bytearray_add_self.py.exp+1 0 added
    @@ -0,0 +1 @@
    +bytearray(b'123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789')
    
  • tests/basics/bytearray_slice_assign.py+12 6 modified
    @@ -18,7 +18,7 @@
     l[1:3] = bytearray()
     print(l)
     l = bytearray(x)
    -#del l[1:3]
    +# del l[1:3]
     print(l)
     
     l = bytearray(x)
    @@ -28,7 +28,7 @@
     l[:3] = bytearray()
     print(l)
     l = bytearray(x)
    -#del l[:3]
    +# del l[:3]
     print(l)
     
     l = bytearray(x)
    @@ -38,7 +38,7 @@
     l[:-3] = bytearray()
     print(l)
     l = bytearray(x)
    -#del l[:-3]
    +# del l[:-3]
     print(l)
     
     # slice assignment that extends the array
    @@ -61,8 +61,14 @@
     print(b)
     
     # Growth of bytearray via slice extension
    -b = bytearray(b'12345678')
    -b.append(57) # expand and add a bit of unused space at end of the bytearray
    +b = bytearray(b"12345678")
    +b.append(57)  # expand and add a bit of unused space at end of the bytearray
     for i in range(400):
    -    b[-1:] = b'ab' # grow slowly into the unused space
    +    b[-1:] = b"ab"  # grow slowly into the unused space
    +print(len(b), b)
    +
    +# Growth of bytearray via slice extension from itself
    +b = bytearray(b"1234567")
    +for i in range(3):
    +    b[-1:] = b
     print(len(b), b)
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.