From de44492d7d6d9235d240259cd2091c03ac5122d1 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sat, 27 Apr 2024 17:18:39 +0100 Subject: [PATCH 1/7] gh-116767: fix crash on 'async with' with many context managers --- Lib/test/test_syntax.py | 25 +++++++++++++++++++++++++ Python/compile.c | 15 ++++++++++++++- Python/flowgraph.c | 2 +- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index e9bec3317811dd..491cd8bb7daa42 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2402,6 +2402,31 @@ def bug(): with self.subTest(f"out of range: {n=}"): self._check_error(get_code(n), "too many statically nested blocks") + @support.cpython_only + def test_async_with_statement_many_context_managers(self): + # See gh-116767 + + def get_code(n): + code = [ textwrap.dedent(""" + async def bug(): + async with ( + a + """) ] + for i in range(n): + code.append(f" as a{i}, a\n") + code.append("): yield a") + return "".join(code) + + CO_MAXBLOCKS = 20 # static nesting limit of the compiler + + for n in range(CO_MAXBLOCKS): + with self.subTest(f"within range: {n=}"): + compile(get_code(n), "", "exec") + + for n in range(CO_MAXBLOCKS, CO_MAXBLOCKS + 5): + with self.subTest(f"out of range: {n=}"): + self._check_error(get_code(n), "too many statically nested blocks") + def test_barry_as_flufl_with_syntax_errors(self): # The "barry_as_flufl" rule can produce some "bugs-at-a-distance" if # is reading the wrong token in the presence of syntax errors later diff --git a/Python/compile.c b/Python/compile.c index ca5551a8e64ab0..370c773cad70a6 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -113,7 +113,8 @@ compiler IR. enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END, WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER, - EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR }; + EXCEPTION_GROUP_HANDLER, ASYNC_COMPREHENSION_GENERATOR, + STOP_ITERATION }; struct fblockinfo { enum fblocktype fb_type; @@ -1503,6 +1504,7 @@ compiler_unwind_fblock(struct compiler *c, location *ploc, case EXCEPTION_HANDLER: case EXCEPTION_GROUP_HANDLER: case ASYNC_COMPREHENSION_GENERATOR: + case STOP_ITERATION: return SUCCESS; case FOR_LOOP: @@ -2232,6 +2234,16 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f c->u->u_metadata.u_argcount = asdl_seq_LEN(args->args); c->u->u_metadata.u_posonlyargcount = asdl_seq_LEN(args->posonlyargs); c->u->u_metadata.u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs); + + NEW_JUMP_TARGET_LABEL(c, start); + USE_LABEL(c, start); + if (c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator) { + /* wrap_in_stopiteration_handler will push a block, so we need to account for that */ + RETURN_IF_ERROR( + compiler_push_fblock(c, NO_LOCATION, STOP_ITERATION, + start, NO_LABEL, NULL)); + } + for (Py_ssize_t i = first_instr; i < asdl_seq_LEN(body); i++) { VISIT_IN_SCOPE(c, stmt, (stmt_ty)asdl_seq_GET(body, i)); } @@ -2240,6 +2252,7 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f compiler_exit_scope(c); return ERROR; } + compiler_pop_fblock(c, STOP_ITERATION, start); } PyCodeObject *co = optimize_and_assemble(c, 1); compiler_exit_scope(c); diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 83768023a4d870..9a9689f6492a3b 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -263,7 +263,7 @@ basicblock_insert_instruction(basicblock *block, int pos, cfg_instr *instr) { } /* For debugging purposes only */ -#if 0 +#if 1 static void dump_instr(cfg_instr *i) { From 058faa3c6b78ac5d6c3e53120109261d3a6cc5cb Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 27 Apr 2024 16:23:32 +0000 Subject: [PATCH 2/7] =?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 --- .../2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst new file mode 100644 index 00000000000000..cec2041d12ee15 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-27-16-23-29.gh-issue-116767.z9UFpr.rst @@ -0,0 +1 @@ +Fix crash in compiler on 'async with' that has many context managers. From 4e4328fc75b0bae2b27ad9fea99b6359e0769e99 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sat, 27 Apr 2024 18:39:46 +0100 Subject: [PATCH 3/7] revert debug thing --- Python/flowgraph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 9a9689f6492a3b..83768023a4d870 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -263,7 +263,7 @@ basicblock_insert_instruction(basicblock *block, int pos, cfg_instr *instr) { } /* For debugging purposes only */ -#if 1 +#if 0 static void dump_instr(cfg_instr *i) { From 42a752fafe45adc4690ed257d6b2db61321a4e95 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sat, 27 Apr 2024 19:47:13 +0100 Subject: [PATCH 4/7] fix test --- Lib/test/test_syntax.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 491cd8bb7daa42..a0743782519173 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2418,12 +2418,13 @@ async def bug(): return "".join(code) CO_MAXBLOCKS = 20 # static nesting limit of the compiler + MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block - for n in range(CO_MAXBLOCKS): + for n in range(MAX_MANAGERS): with self.subTest(f"within range: {n=}"): compile(get_code(n), "", "exec") - for n in range(CO_MAXBLOCKS, CO_MAXBLOCKS + 5): + for n in range(MAX_MANAGERS, MAX_MANAGERS + 5): with self.subTest(f"out of range: {n=}"): self._check_error(get_code(n), "too many statically nested blocks") From 2e8aab2bc7784a253b3fdb5c381c553eb706eaed Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sat, 27 Apr 2024 21:15:21 +0100 Subject: [PATCH 5/7] fix other test --- Lib/test/test_syntax.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index a0743782519173..c9f2714c0bbce4 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2393,12 +2393,13 @@ def bug(): return code CO_MAXBLOCKS = 20 # static nesting limit of the compiler + MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block - for n in range(CO_MAXBLOCKS): + for n in range(MAX_MANAGERS): with self.subTest(f"within range: {n=}"): compile(get_code(n), "", "exec") - for n in range(CO_MAXBLOCKS, CO_MAXBLOCKS + 5): + for n in range(MAX_MANAGERS, MAX_MANAGERS + 5): with self.subTest(f"out of range: {n=}"): self._check_error(get_code(n), "too many statically nested blocks") From f29d7bf02e642096cd8b2c8fe3160a890cd45031 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Mon, 29 Apr 2024 17:03:25 +0100 Subject: [PATCH 6/7] increment CO_MAXBLOCKS --- Include/cpython/code.h | 2 +- Lib/test/test_syntax.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index b0e226e0e1971a..ef8f9304ccab56 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -226,7 +226,7 @@ struct PyCodeObject _PyCode_DEF(1); */ #define PY_PARSER_REQUIRES_FUTURE_KEYWORD -#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */ +#define CO_MAXBLOCKS 21 /* Max static block nesting within a function */ PyAPI_DATA(PyTypeObject) PyCode_Type; diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index c9f2714c0bbce4..de783f714509a3 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2392,7 +2392,7 @@ def bug(): code += "): yield a" return code - CO_MAXBLOCKS = 20 # static nesting limit of the compiler + CO_MAXBLOCKS = 21 # static nesting limit of the compiler MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block for n in range(MAX_MANAGERS): @@ -2418,7 +2418,7 @@ async def bug(): code.append("): yield a") return "".join(code) - CO_MAXBLOCKS = 20 # static nesting limit of the compiler + CO_MAXBLOCKS = 21 # static nesting limit of the compiler MAX_MANAGERS = CO_MAXBLOCKS - 1 # One for the StopIteration block for n in range(MAX_MANAGERS): @@ -2563,7 +2563,8 @@ def test_syntax_error_on_deeply_nested_blocks(self): while 20: while 21: while 22: - break + while 23: + break """ self._check_error(source, "too many statically nested blocks") From 39d64f2e8fe1e46a18eaf2d225c7bb0bcee4175e Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Mon, 29 Apr 2024 17:09:07 +0100 Subject: [PATCH 7/7] calculate once --- Python/compile.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 370c773cad70a6..4e94f9297a32d1 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2237,7 +2237,8 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f NEW_JUMP_TARGET_LABEL(c, start); USE_LABEL(c, start); - if (c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator) { + bool add_stopiteration_handler = c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator; + if (add_stopiteration_handler) { /* wrap_in_stopiteration_handler will push a block, so we need to account for that */ RETURN_IF_ERROR( compiler_push_fblock(c, NO_LOCATION, STOP_ITERATION, @@ -2247,7 +2248,7 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f for (Py_ssize_t i = first_instr; i < asdl_seq_LEN(body); i++) { VISIT_IN_SCOPE(c, stmt, (stmt_ty)asdl_seq_GET(body, i)); } - if (c->u->u_ste->ste_coroutine || c->u->u_ste->ste_generator) { + if (add_stopiteration_handler) { if (wrap_in_stopiteration_handler(c) < 0) { compiler_exit_scope(c); return ERROR;