From f24987a0aecf9eb1eb666fcc22ea18c04fbc9bbd Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:48:54 +0800 Subject: [PATCH 1/6] Add a null check for attribute promotion --- Lib/test/test_capi/test_opt.py | 34 ++++++++++++++++++++++++++++++++++ Python/optimizer_analysis.c | 4 ++++ Python/optimizer_bytecodes.c | 8 +++++++- Python/optimizer_cases.c.h | 7 ++++++- 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 3fc2cb33795a5f..7befc0d76fb83b 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2305,6 +2305,40 @@ def testfunc(n): self.assertNotIn("_GUARD_TOS_INT", uops) self.assertNotIn("_GUARD_NOS_INT", uops) + def test_attr_promotion_failure(self): + # We're not testing for any specific uops here, just + # testing it doesn't crash. + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + import _testinternalcapi + import opcode + import _opcode + + def get_first_executor(func): + code = func.__code__ + co_code = code.co_code + for i in range(0, len(co_code), 2): + try: + return _opcode.get_executor(code, i) + except ValueError: + pass + return None + + def get_opnames(ex): + return {item[0] for item in ex} + + import email + + def testfunc(n): + for _ in range(n): + email.jit_testing = None + prompt = email.jit_testing + del email.jit_testing + + + testfunc(_testinternalcapi.TIER2_THRESHOLD) + """), PYTHON_JIT="1") + self.assertEqual(result[0].rc, 0, result) + def global_identity(x): return x diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 6a7df233819b9c..321957ce73efe3 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -103,6 +103,10 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop) if ((int)index >= dict->ma_keys->dk_nentries) { return NULL; } + PyDictKeysObject *keys = dict->ma_keys; + if (keys->dk_version != inst->operand0) { + return NULL; + } PyObject *res = entries[index].me_value; if (res == NULL) { return NULL; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 5a9a3a943a7b02..d655adf6f774b2 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -557,7 +557,13 @@ dummy_func(void) { PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); PyObject *res = convert_global_to_const(this_instr, dict, true); - attr = sym_new_const(ctx, res); + if (res == NULL) { + attr = sym_new_not_null(ctx); + } + else { + attr = sym_new_const(ctx, res); + } + } } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 8c4f0399c75a73..f35cee3ba1ca30 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1205,7 +1205,12 @@ PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); PyObject *res = convert_global_to_const(this_instr, dict, true); - attr = sym_new_const(ctx, res); + if (res == NULL) { + attr = sym_new_not_null(ctx); + } + else { + attr = sym_new_const(ctx, res); + } } } } From 305bbad3e378a8a5b41132ffac76743c41b9d132 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 12:50:55 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst new file mode 100644 index 00000000000000..a65a0c85fa64fa --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst @@ -0,0 +1 @@ +Fix a crash in the JIT involving attributes of modules. From 1f35cc8c31eeca0a391d5c80ab67b74b0a359434 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:56:50 +0800 Subject: [PATCH 3/6] lint --- Lib/test/test_capi/test_opt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 7befc0d76fb83b..fe91eec79b0c20 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2327,7 +2327,7 @@ def get_opnames(ex): return {item[0] for item in ex} import email - + def testfunc(n): for _ in range(n): email.jit_testing = None From 092ecc5545e191632bcc4c153c8338e1f230c37d Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 17 Jun 2025 21:39:01 +0800 Subject: [PATCH 4/6] Update test_opt.py Co-Authored-By: devdanzin <74280297+devdanzin@users.noreply.github.com> --- Lib/test/test_capi/test_opt.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index fe91eec79b0c20..59bc5c30d06653 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2312,6 +2312,7 @@ def test_attr_promotion_failure(self): import _testinternalcapi import opcode import _opcode + import email def get_first_executor(func): code = func.__code__ @@ -2326,8 +2327,6 @@ def get_first_executor(func): def get_opnames(ex): return {item[0] for item in ex} - import email - def testfunc(n): for _ in range(n): email.jit_testing = None From 97e96ba3bcfd851afb88f3db3241e5dbe500beef Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 19 Jun 2025 18:36:59 +0800 Subject: [PATCH 5/6] Address review --- Lib/test/test_capi/test_opt.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 59bc5c30d06653..403bea029f17a0 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2310,7 +2310,6 @@ def test_attr_promotion_failure(self): # testing it doesn't crash. result = script_helper.run_python_until_end('-c', textwrap.dedent(""" import _testinternalcapi - import opcode import _opcode import email @@ -2324,9 +2323,6 @@ def get_first_executor(func): pass return None - def get_opnames(ex): - return {item[0] for item in ex} - def testfunc(n): for _ in range(n): email.jit_testing = None @@ -2335,6 +2331,8 @@ def testfunc(n): testfunc(_testinternalcapi.TIER2_THRESHOLD) + ex = get_first_executor(testfunc) + assert ex is not None """), PYTHON_JIT="1") self.assertEqual(result[0].rc, 0, result) From f3d998760ecc527943e8ddf6ce98b3d2a09eab34 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 20 Jun 2025 11:12:25 +0800 Subject: [PATCH 6/6] Address review --- Lib/test/test_capi/test_opt.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 403bea029f17a0..f9070ee8010fe4 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2308,7 +2308,7 @@ def testfunc(n): def test_attr_promotion_failure(self): # We're not testing for any specific uops here, just # testing it doesn't crash. - result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + script_helper.assert_python_ok('-c', textwrap.dedent(""" import _testinternalcapi import _opcode import email @@ -2333,8 +2333,7 @@ def testfunc(n): testfunc(_testinternalcapi.TIER2_THRESHOLD) ex = get_first_executor(testfunc) assert ex is not None - """), PYTHON_JIT="1") - self.assertEqual(result[0].rc, 0, result) + """)) def global_identity(x):