Skip to content

Commit 7cfe83f

Browse files
committed
gh-159672: Keep typo budgets off the normal parser path
1 parent 565f103 commit 7cfe83f

2 files changed

Lines changed: 25 additions & 9 deletions

File tree

‎Parser/pegen.c‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,19 @@ _PyPegen_Parser_Free(Parser *p)
10451045
PyMem_Free(p);
10461046
}
10471047

1048+
int
1049+
_PyPegen_stack_exhausted_slow(Parser *p)
1050+
{
1051+
if (p->keyword_trial != NULL) {
1052+
if (p->keyword_trial->budget == 0) {
1053+
return 1;
1054+
}
1055+
p->keyword_trial->budget--;
1056+
p->keyword_trial->furthest = Py_MAX(p->keyword_trial->furthest, p->mark);
1057+
}
1058+
return _Py_ReachedRecursionLimitWithMargin(p->tstate, 1);
1059+
}
1060+
10481061
static void
10491062
reset_parser_state_for_error_pass(Parser *p)
10501063
{
@@ -1156,6 +1169,14 @@ try_keyword_typo(Parser *p, KeywordTypo *candidate)
11561169
trial.error_indicator = 0;
11571170
trial.call_invalid_rules = 0;
11581171
trial.keyword_trial = &state;
1172+
// Force trials through the existing stack-check slow path to count rule
1173+
// entries without adding a branch to normal parsing. The slow path still
1174+
// checks the thread's real stack limit.
1175+
#if _Py_STACK_GROWS_DOWN
1176+
trial.stack_soft_limit = UINTPTR_MAX;
1177+
#else
1178+
trial.stack_soft_limit = 0;
1179+
#endif
11591180
trial.known_err_token = NULL;
11601181
trial.errcode = NULL;
11611182
trial.identifier_cache = identifiers;

‎Parser/pegen.h‎

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,14 @@ typedef struct {
111111
uintptr_t stack_soft_limit;
112112
} Parser;
113113

114+
int _PyPegen_stack_exhausted_slow(Parser *p);
115+
114116
// C-stack exhaustion check run on every rule entry. The fast path compares
115117
// the machine stack pointer against the soft limit cached in the Parser;
116-
// only when the stack is nearly exhausted is the full check called.
118+
// trials force the slow path to account for their rule budget.
117119
static inline int
118120
_PyPegen_stack_exhausted(Parser *p)
119121
{
120-
if (p->keyword_trial != NULL) {
121-
if (p->keyword_trial->budget == 0) {
122-
return 1;
123-
}
124-
p->keyword_trial->budget--;
125-
p->keyword_trial->furthest = Py_MAX(p->keyword_trial->furthest, p->mark);
126-
}
127122
#if _Py_STACK_GROWS_DOWN
128123
if (_Py_get_machine_stack_pointer() > p->stack_soft_limit) {
129124
#else
@@ -132,7 +127,7 @@ _PyPegen_stack_exhausted(Parser *p)
132127
assert(!_Py_ReachedRecursionLimitWithMargin(p->tstate, 1));
133128
return 0;
134129
}
135-
return _Py_ReachedRecursionLimitWithMargin(p->tstate, 1);
130+
return _PyPegen_stack_exhausted_slow(p);
136131
}
137132

138133
typedef struct {

0 commit comments

Comments
 (0)