Skip to content

gh-152166: Fix array.array.fromlist() exposing uninitialized memory when an element's __index__ resizes the array#152167

Open
iamsharduld wants to merge 1 commit into
python:mainfrom
iamsharduld:local-gh-152166-array-fromlist-uninit
Open

gh-152166: Fix array.array.fromlist() exposing uninitialized memory when an element's __index__ resizes the array#152167
iamsharduld wants to merge 1 commit into
python:mainfrom
iamsharduld:local-gh-152166-array-fromlist-uninit

Conversation

@iamsharduld

@iamsharduld iamsharduld commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Fixes the uninitialized-memory exposure in array.array.fromlist() reported in #152166.

fromlist() preallocated n slots with array_resize(self, old_size + n) and then filled them at Py_SIZE(self) - n + i, recomputed from the live Py_SIZE each iteration, while only guarding against the source list changing size. When an element's __index__ resized self as a side effect of the setitem callback, the write index slid forward, the reserved slots were left unwritten, and the array returned uninitialized heap memory (with the real items misplaced) on a successful return — array_resize uses PyMem_RESIZE with no zeroing.

This writes the reserved slot old_size + i directly and raises RuntimeError("array changed size during iteration") if self is resized mid-iteration, mirroring the adjacent list-mutation guard.

Distinct from #144128 / #144138, which fixed a use-after-free in the II/LL/QQ_setitem conversion helpers and did not touch fromlist's index logic; this defect remained on main.

Before

import array
a = array.array('i')
class Evil:
    def __index__(self):
        a.extend([0]*5)
        return 111
a.fromlist([Evil(), 222, 333])
print(a.tolist())
# debug build: [111, -842150451, -842150451, 0, 0, 0, 222, 333]
#                    ^^^^^^^^^^^  ^^^^^^^^^^  0xCDCDCDCD = uninitialized

After

RuntimeError: array changed size during iteration   # array left unchanged

Adds a regression test (test_fromlist_reentrant_self_resize) covering grow and shrink across the signed/unsigned integer typecodes.

…mory on reentrant resize

array.array.fromlist() preallocated n slots with array_resize() and then
filled them at an index recomputed from the live Py_SIZE(self) each
iteration, guarding only against the source list changing size.  When an
element's __index__ resized self as a side effect of the setitem call, the
write index slid forward, the reserved slots were left unwritten, and the
array exposed uninitialized heap memory (with the items misplaced) on a
successful return.

Fill the fixed slot old_size + i instead, and raise RuntimeError if self is
resized mid-iteration, mirroring the existing list-mutation guard.

This is distinct from pythongh-144128/pythongh-144138, which fixed a use-after-free in
the *_setitem conversion helpers and did not touch fromlist's index logic.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant