MicroPython objarray.c use after free
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.
| Package | Affected versions | Patched versions |
|---|---|---|
micropython-copyPyPI | <= 3.3.3.post3 | — |
micropython-ioPyPI | <= 0.1 | — |
Affected products
4- ghsa-coords3 versionspkg:pypi/micropython-copypkg:pypi/micropython-iopkg:rpm/opensuse/micropython&distro=openSUSE%20Tumbleweed
<= 3.3.3.post3+ 2 more
- (no CPE)range: <= 3.3.3.post3
- (no CPE)range: <= 0.1
- (no CPE)range: < 1.28.0-2.1
Patches
14bed614e707cpy/objarray: Fix use-after-free if extending a bytearray from itself.
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- github.com/micropython/micropython/commit/4bed614e707c0644c06e117f848fa12605c711cdghsapatchWEB
- github.com/micropython/micropython/releases/tag/v1.23.0ghsapatchWEB
- github.com/advisories/GHSA-pwwp-3q7j-9mx8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-8947ghsaADVISORY
- vuldb.comghsathird-party-advisoryWEB
- github.com/micropython/micropython/issues/13283ghsaissue-trackingWEB
- github.com/micropython/micropython/issues/13283ghsaissue-trackingWEB
- github.com/pypa/advisory-database/tree/main/vulns/micropython-copy/PYSEC-2024-92.yamlghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/micropython-io/PYSEC-2024-94.yamlghsaWEB
- vuldb.comghsasignaturepermissions-requiredWEB
- vuldb.comghsavdb-entryWEB
News mentions
0No linked articles in our index yet.