-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Setting state of an exception object with a dic crashes Python 3.8.14 #97591
Comments
I ran this in main under the debugger and it looks like the crash actually occurs during final cleanup, in ClearWeakRefs called from free_keys_object. Not sure if I have the guts to continue debugging. |
Reproduced this in python 3.10.2, and latest main (83a3de4) I would try to dig a little deeper but can't guarantee there will be any output (my knowledge about the python runtime is limited). |
I debugged with following code and found:
import gc
class Key(str):
def __hash__(self):
d.clear()
return 0
class Value(str):
pass
d = {}
d[Key()] = Value()
e = Exception()
e.__setstate__(d)
gc.collect() # Segfault happens here According the stacktrace, it dies in Lines 1095 to 1103 in ff54dd9
And finally Lines 409 to 411 in ff54dd9
|
It seems that crash happening during garbage collector subtracting internal references: Lines 465 to 482 in ff54dd9
While traversing through the objects, it meets an object, and the
Lines 448 to 450 in ff54dd9
Then it wants to dump the parent (the dict in this scenario) with message, but segfault happens during the process. (During printing the representation of the value. Strange.) Line 2416 in 9f2f1dd
|
What confused me is that, if I comment out the Does the |
Find the cause of wrongly set refcount: Lines 4017 to 4032 in 9f2f1dd
The So the problem is in Lines 1370 to 1390 in ff54dd9
Full stack trace when the refcount is changed.libc.so.6!__memset_avx2_unaligned_erms (Unknown Source:0) |
So... What should we do with it? I think I need some guidance/reference now, about what |
Thanks for the deep research! I think the root cause is something that happens in while (PyDict_Next(state, &i, &d_key, &d_value)) {
if (PyObject_SetAttr(self, d_key, d_value) < 0)
return NULL;
} are likely to end up missing an INCREF when the dict is cleared by the hash call. I suspect it's happening in the Looking at the code for |
I tried to play with it, adding I wonder how could I build and inspect make -s -j
# make: *** [Makefile:1306: Python/deepfreeze/deepfreeze.c] Segmentation fault (core dumped) |
It is indeed missing an incref before calling The following patch fixes it: diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index 3703fdcda4..3680c6068b 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -167,8 +167,14 @@ BaseException_setstate(PyObject *self, PyObject *state)
return NULL;
}
while (PyDict_Next(state, &i, &d_key, &d_value)) {
- if (PyObject_SetAttr(self, d_key, d_value) < 0)
+ Py_INCREF(d_key);
+ Py_INCREF(d_value);
+ int res = PyObject_SetAttr(self, d_key, d_value);
+ Py_DECREF(d_key);
+ Py_DECREF(d_value);
+ if (res < 0) {
return NULL;
+ }
}
}
Py_RETURN_NONE;
|
Wow, I'll check the issue. With this patch, the bug disappeared. Should I make a PR for the patch? @kumaraditya303 I guess it's fixed before, but the patch is reversed for some reason. I would look into the line history. In the latest main, the inc/dec ref pair is still missing: Lines 158 to 175 in 273a819
|
Feel free to create a PR with tests. |
I check the line history but didn't find much useful information. These lines seem to be unchanged since the cpython repo migrated from SVN in 2006? This is the commit. git log -L '/PyObject_SetAttr(self, d_key, d_value)/,+5:Objects/exceptions.c'
commit 4d70c3d9dded0f0fa7a73c67217a71111d05df4d
Author: Thomas Wouters <thomas@python.org>
Date: Thu Jun 8 14:42:34 2006 +0000
....
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -167,0 +170,5 @@
+ if (PyObject_SetAttr(self, d_key, d_value) < 0)
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE; |
I would create a test triggering this behavior in |
PR is created.
cpython/Lib/test/test_baseexception.py Lines 7 to 10 in 273a819
|
…es before calling `tp_hash` slot (pythonGH-97700) (cherry picked from commit d639438) Co-authored-by: Ofey Chan <ofey206@gmail.com>
…ore calling `tp_hash` slot (#97700)
…es before calling `tp_hash` slot (pythonGH-97700) (cherry picked from commit d639438) Co-authored-by: Ofey Chan <ofey206@gmail.com>
Thanks for your investigation and patch! |
…es before calling `tp_hash` slot (python#97700)
* main: (2069 commits) pythongh-96512: Move int_max_str_digits setting to PyConfig (python#96944) pythongh-94808: Coverage: Check picklablability of calliter (python#95923) pythongh-94808: Add test coverage for PyObject_HasAttrString (python#96627) pythongh-94732: Fix KeyboardInterrupt race in asyncio run_forever() (python#97765) Fix typos in `bltinmodule.c`. (pythonGH-97766) pythongh-94808: `_PyLineTable_StartsLine` was not used (pythonGH-96609) pythongh-97681: Remove Tools/demo/ directory (python#97682) Fix typo in unittest docs (python#97742) pythongh-97728: Argument Clinic: Fix uninitialized variable in the Py_UNICODE converter (pythonGH-97729) pythongh-95913: Fix PEP number in PEP 678 What's New ref label (python#97739) pythongh-95913: Copyedit/improve New Modules What's New section (python#97721) pythongh-97740: Fix bang in Sphinx C domain ref target syntax (python#97741) pythongh-96819: multiprocessing.resource_tracker: check if length of pipe write <= 512 (python#96890) pythongh-97706: multiprocessing tests: Delete unused variable `rand` (python#97707) pythonGH-85447: Clarify docs about awaiting future multiple times (python#97738) [docs] Update logging cookbook with recipe for using a logger like an output… (pythonGH-97730) pythongh-97607: Fix content parsing in the impl-detail reST directive (python#97652) pythongh-95975: Move except/*/finally ref labels to more precise locations (python#95976) pythongh-97591: In `Exception.__setstate__()` acquire strong references before calling `tp_hash` slot (python#97700) pythongh-95588: Drop the safety claim from `ast.literal_eval` docs. (python#95919) ...
Crash report
The following programs defined a class C. In C, we perform dic clear() operation. When we set state of exception object with the dic, it causes segmentation fault on main branch (68c46ae) and latest stable Python 3.8.14. But it does not trigger any crashing on Python-3.11.0rc2 and Python 3.9.0.
Error messages
Segmentation Fault (Core dumped)
Your environment
The text was updated successfully, but these errors were encountered: