https://github.com/python/cpython/commit/b07a267953b35f36f433b3078b2f6c89b95c72b9
commit: b07a267953b35f36f433b3078b2f6c89b95c72b9
branch: main
author: Peter Bierma <[email protected]>
committer: markshannon <[email protected]>
date: 2025-08-19T09:53:38+01:00
summary:
gh-137883: Check the recursion limit for specialized keyword argument calls
(GH-137887)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-08-17-13-36-53.gh-issue-137883.55VDCN.rst
M Include/internal/pycore_opcode_metadata.h
M Lib/test/test_call.py
M Python/bytecodes.c
M Python/generated_cases.c.h
diff --git a/Include/internal/pycore_opcode_metadata.h
b/Include/internal/pycore_opcode_metadata.h
index 7f468bbb932184..bd6b84ec7fd908 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -1354,7 +1354,7 @@ _PyOpcode_macro_expansion[256] = {
[CALL_ISINSTANCE] = { .nuops = 3, .uops = { { _GUARD_THIRD_NULL,
OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_ISINSTANCE, OPARG_SIMPLE, 3 }, {
_CALL_ISINSTANCE, OPARG_SIMPLE, 3 } } },
[CALL_KW_BOUND_METHOD] = { .nuops = 6, .uops = { { _CHECK_PEP_523,
OPARG_SIMPLE, 1 }, { _CHECK_METHOD_VERSION_KW, 2, 1 }, { _EXPAND_METHOD_KW,
OPARG_SIMPLE, 3 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET,
OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
[CALL_KW_NON_PY] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE_KW,
OPARG_SIMPLE, 3 }, { _CALL_KW_NON_PY, OPARG_SIMPLE, 3 }, {
_CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } },
- [CALL_KW_PY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1
}, { _CHECK_FUNCTION_VERSION_KW, 2, 1 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, {
_SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME,
OPARG_SIMPLE, 3 } } },
+ [CALL_KW_PY] = { .nuops = 6, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1
}, { _CHECK_FUNCTION_VERSION_KW, 2, 1 }, { _CHECK_RECURSION_REMAINING,
OPARG_SIMPLE, 3 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET,
OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
[CALL_LEN] = { .nuops = 3, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 },
{ _GUARD_CALLABLE_LEN, OPARG_SIMPLE, 3 }, { _CALL_LEN, OPARG_SIMPLE, 3 } } },
[CALL_LIST_APPEND] = { .nuops = 4, .uops = { {
_GUARD_CALLABLE_LIST_APPEND, OPARG_SIMPLE, 3 }, { _GUARD_NOS_NOT_NULL,
OPARG_SIMPLE, 3 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 3 }, { _CALL_LIST_APPEND,
OPARG_SIMPLE, 3 } } },
[CALL_METHOD_DESCRIPTOR_FAST] = { .nuops = 2, .uops = { {
_CALL_METHOD_DESCRIPTOR_FAST, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END,
OPARG_REPLACED, 3 } } },
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 1c73aaafb71fd5..2c28f106ec7cb6 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -1074,6 +1074,14 @@ def c_py_recurse(m):
with self.assertRaises(RecursionError):
c_py_recurse(100_000)
+ def test_recursion_with_kwargs(self):
+ # GH-137883: The interpreter forgot to check the recursion limit when
+ # calling with keywords.
+ def recurse_kw(a=0):
+ recurse_kw(a=0)
+ with self.assertRaises(RecursionError):
+ recurse_kw()
+
class TestFunctionWithManyArgs(unittest.TestCase):
def test_function_with_many_args(self):
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-17-13-36-53.gh-issue-137883.55VDCN.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-17-13-36-53.gh-issue-137883.55VDCN.rst
new file mode 100644
index 00000000000000..0bdb2638c86884
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-17-13-36-53.gh-issue-137883.55VDCN.rst
@@ -0,0 +1 @@
+Fix runaway recursion when calling a function with keyword arguments.
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 8a60e48cd465b5..21bae689320eae 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -4721,6 +4721,7 @@ dummy_func(
unused/1 + // Skip over the counter
_CHECK_PEP_523 +
_CHECK_FUNCTION_VERSION_KW +
+ _CHECK_RECURSION_REMAINING +
_PY_FRAME_KW +
_SAVE_RETURN_OFFSET +
_PUSH_FRAME;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index d63225bb0cd3cd..b6d183a3e63a9c 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -3277,6 +3277,14 @@
JUMP_TO_PREDICTED(CALL_KW);
}
}
+ // _CHECK_RECURSION_REMAINING
+ {
+ if (tstate->py_recursion_remaining <= 1) {
+ UPDATE_MISS_STATS(CALL_KW);
+ assert(_PyOpcode_Deopt[opcode] == (CALL_KW));
+ JUMP_TO_PREDICTED(CALL_KW);
+ }
+ }
// _PY_FRAME_KW
{
kwnames = stack_pointer[-1];
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]