Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Include/internal/pycore_uop_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import copy
import pickle
import dis
import sys
import threading
import types
import unittest
Expand Down Expand Up @@ -2171,6 +2172,39 @@ class MyList(list): pass
self.assert_no_opcode(my_list_append, "CALL_LIST_APPEND")
self.assert_no_opcode(my_list_append, "CALL")

@cpython_only
@requires_specialization
def test_call_list_append_memory_error(self):
# gh-151818: if CALL_LIST_APPEND fails to resize the list,
# the argument must be released exactly once.
_testcapi = import_module("_testcapi")

class Holder:
pass

def list_append(l, holder):
# holder.item is pushed as a new reference, which
# CALL_LIST_APPEND steals.
l.append(holder.item)

holder = Holder()
holder.item = item = object()
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
list_append([], holder)
self.assert_specialized(list_append, "CALL_LIST_APPEND")

refcount = sys.getrefcount(item)
# An empty list has no spare capacity, so append() must resize it.
lst = []
with self.assertRaises(MemoryError):
_testcapi.set_nomemory(0)
try:
list_append(lst, holder)
finally:
_testcapi.remove_mem_hooks()
self.assertEqual(lst, [])
self.assertEqual(sys.getrefcount(item), refcount)

@cpython_only
@requires_specialization
def test_call_c_function_extra_flags(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a double-free crash in ``list.append()`` (the specialized
``CALL_LIST_APPEND`` instruction) when growing the list fails with
:exc:`MemoryError`.
16 changes: 16 additions & 0 deletions Modules/_testinternalcapi/test_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -5038,7 +5038,10 @@ dummy_func(
int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg));
UNLOCK_OBJECT(self_o);
if (err) {
ERROR_NO_POP();
// arg is consumed even on failure: close the other inputs
// and pop them all, so that arg is not closed a second time.
DECREF_INPUTS();
ERROR_IF(true);
}
c = callable;
s = self;
Expand Down
62 changes: 58 additions & 4 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading