You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
commit 6f3c138dfa868b32d3288898923bbfa388f2fa5d
Author: Serhiy Storchaka <storchaka@gmail.com>
Date: Wed Sep 6 23:55:42 2023 +0300
gh-108751: Add copy.replace() function (GH-108752)
It creates a modified copy of an object by calling the object's
__replace__() method.
It is a generalization of dataclasses.replace(), named tuple's _replace()
method and replace() methods in various classes, and supports all these
stdlib classes.
$ make && ./python -m test test_sys_settrace -R 3:3
(...)
Total duration: 18.8 sec
Total tests: run=439 skipped=1
Total test files: run=1/1
Result: SUCCESS
The ultimate issue for this lives in code_richcompare where it compares the opcodes one by one of the code objects. However, when the code object is instrumented, the opcodes would be INSTRUMENTED_LINE or INSTRUMENTED_INSTRUCTION, so the comparasion would be invalid. Moreover, the opcode to be compared should be the main opcode (not the cache entries), which relies on a correct calculation of cache entry number of the current opcode. As the opcode becomes INSTRUMENTED_INSTRUCTION, that number to skip the cache is wrong too, which caused the SegFault.
The crash requires a specific pattern, in this case, a cache entry with the opcode field that happened to have ENTER_EXECUTOR. Obviously no executors would be there and there's a NULL dereference. So the bisected commit did not introduce the issue, it might just be the trigger for this crash. The issue was there for a long time.
There's an interesting test to run:
importsyscode1=compile("x is x", "example.py", "eval")
code2=compile("x in x", "example.py", "eval")
sys._getframe().f_trace_opcodes=Truesys.settrace(lambda*args: None)
exec(code1, {"x": []})
exec(code2, {"x": []})
print(code1==code2)
sys.settrace(None)
You'll find that code1 == code2, even though they are obviously not the same thing. Because the instructions are instrumented and are considered the same.
For now, I directly stripped the instrumentation for the opcode, which resolves the crashing issue and the case above (regression test added in test_code). However, this implementation means we do not care whether the code is instrumented or not. So for two "same" code objects, if one of them is instrumented and the other is not, we still consider them "the same".
I don't know the correct answer to this, are they the same? We can easily add a check for the instrumented instructions, it's not difficult, but we probably need to determine the "correct answer" for that first.
Is it possible to compare the original bytecode on code1==code2, rather than the instrumented bytecode?
I think @markshannon proposed an alternative, remove code comparison operator. I recall that i worked on that operator for a strange statement building two code objects on the same line. The regression test should be somewhere in test_compile, I suppose.
Activity
vstinner commentedon Sep 7, 2023
Reverting the codeobject.c is enough to workaround the regression:
With this revert:
cc @serhiy-storchaka
serhiy-storchaka commentedon Sep 7, 2023
I do not understand how adding an alias of an existing method, which is not even used here, can cause a crash.
cc @gaogaotiantian
gaogaotiantian commentedon Sep 7, 2023
I'll take a look at it tomorrow if Mark did not come up with a quick reasoning/solution. Confirmed that the fix for #108976 does not help.
gaogaotiantian commentedon Sep 7, 2023
The ultimate issue for this lives in
code_richcomparewhere it compares the opcodes one by one of the code objects. However, when the code object is instrumented, the opcodes would beINSTRUMENTED_LINEorINSTRUMENTED_INSTRUCTION, so the comparasion would be invalid. Moreover, the opcode to be compared should be the main opcode (not the cache entries), which relies on a correct calculation of cache entry number of the current opcode. As the opcode becomesINSTRUMENTED_INSTRUCTION, that number to skip the cache is wrong too, which caused the SegFault.The crash requires a specific pattern, in this case, a cache entry with the opcode field that happened to have
ENTER_EXECUTOR. Obviously no executors would be there and there's a NULL dereference. So the bisected commit did not introduce the issue, it might just be the trigger for this crash. The issue was there for a long time.There's an interesting test to run:
You'll find that
code1 == code2, even though they are obviously not the same thing. Because the instructions are instrumented and are considered the same.For now, I directly stripped the instrumentation for the opcode, which resolves the crashing issue and the case above (regression test added in
test_code). However, this implementation means we do not care whether the code is instrumented or not. So for two "same" code objects, if one of them is instrumented and the other is not, we still consider them "the same".I don't know the correct answer to this, are they the same? We can easily add a check for the instrumented instructions, it's not difficult, but we probably need to determine the "correct answer" for that first.
@brandtbucher
vstinner commentedon Sep 8, 2023
Is it possible to compare the original bytecode on code1==code2, rather than the instrumented bytecode?
I think @markshannon proposed an alternative, remove code comparison operator. I recall that i worked on that operator for a strange statement building two code objects on the same line. The regression test should be somewhere in test_compile, I suppose.
corona10 commentedon Sep 8, 2023
It's possible.
see : #108188
The approach of #109107 looks good to me.
gh-109052: Use the base opcode when comparing code objects (gh-109107)
vstinner commentedon Sep 10, 2023
I confirm that the bug is fixed:
[3.12] pythongh-109052: Use the base opcode when comparing code objec…
[3.12] GH-109052: Use the base opcode when comparing code objects (GH…