-
-
Notifications
You must be signed in to change notification settings - Fork 31.5k
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
bpo-42609: Check recursion depth in the AST validator and optimizer #23744
bpo-42609: Check recursion depth in the AST validator and optimizer #23744
Conversation
|
||
/* Check that the recursion depth counting balanced correctly */ | ||
if (res && state.recursion_depth != starting_recursion_depth) { | ||
PyErr_Format(PyExc_SystemError, | ||
"AST validator recursion depth mismatch (before=%d, after=%d)", | ||
starting_recursion_depth, state.recursion_depth); | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to check this for every run? I feel like, it is more appropriate when activated only on debug builds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every run? No. But it costs nothing and helped to catch bugs.
Symtable checks balance even in case of failure (res == 0). But supporting it have larger cost, larger chance of making error and is more difficult to test, so I omitted it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not proposing to remove the check altogether, but I couldn't see the reasoning to keep it on the release builds. We have similar checks all over the code (such as asserts()
or blocks guarded with #if PY_DEBUG
) which helps us to catch bugs on the debug builds, and doesn't change anything on the end user's side. Ofc most of them have a certain cost, so I was just asking whether it would be suitable to guard this with PY_DEBUG
or not, but since you said the cost is nothing, guess we can just leave it as is.
Include/compile.h
Outdated
@@ -87,6 +87,9 @@ PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int j | |||
typedef struct { | |||
int optimize; | |||
int ff_features; | |||
|
|||
int recursion_depth; /* current recursion depth */ | |||
int recursion_limit; /* recursion limit */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you not use Py_EnterRecursiveCall here instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is copied from symtable.c
. It uses different recursion limit than in Py_EnterRecursiveCall
. If we decide to use Py_EnterRecursiveCall
, it can be done consistency in symtable.c
, ast.c
and ast_opt.c
. But this is a different issue, for now it is simpler to just duplicate some code three times.
Lib/test/test_compile.py
Outdated
|
||
check_limit("a", "()") | ||
check_limit("a", ".b") | ||
check_limit("a", "[0]") | ||
check_limit("a", "*a") | ||
#check_limit("if a: pass", "\nelif a: pass", mode="exec") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this commented-out code should be added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is still a bug not fixed by this PR.
@@ -0,0 +1,3 @@ | |||
Prevented crashes in the AST validator and optimizer when compile some |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compile -> compiling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
starting_recursion_depth = (tstate->recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? | ||
tstate->recursion_depth * COMPILER_STACK_FRAME_SCALE : tstate->recursion_depth; | ||
state->recursion_depth = starting_recursion_depth; | ||
state->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PEP 7 style guide states that lines should be limited to 79 characters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is copied from symtable.c
and ast.c
. It is better to keep code the same in three places.
details = "Compiling ({!r} + {!r} * {})".format( | ||
prefix, repeated, depth) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An f-string could be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but this code already was here.
Include/compile.h
Outdated
int recursion_depth; /* current recursion depth */ | ||
int recursion_limit; /* recursion limit */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C99 //
comments can be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it was just copied from symtable.c
.
Include/compile.h
Outdated
@@ -87,6 +87,9 @@ PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int j | |||
typedef struct { | |||
int optimize; | |||
int ff_features; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty line separates groups of fields.
PyThreadState *tstate; | ||
int recursion_limit = Py_GetRecursionLimit(); | ||
int starting_recursion_depth; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These declarations can be moved down (C99).
This PR is stale because it has been open for 30 days with no activity. |
Thanks @serhiy-storchaka for the PR 🌮🎉.. I'm working now to backport this PR to: 3.8, 3.9. |
Sorry, @serhiy-storchaka, I could not cleanly backport this to |
Sorry @serhiy-storchaka, I had trouble checking out the |
…izer (pythonGH-23744). (cherry picked from commit face87c) Co-authored-by: Serhiy Storchaka <[email protected]>
GH-25634 is a backport of this pull request to the 3.9 branch. |
https://bugs.python.org/issue42609