From 5893b5db98b38b17750c0572c7209774a5034898 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Mon, 30 May 2022 14:30:15 -0400 Subject: gh-93351: Ensure the position information in AST nodes created by the parser is always consistent (GH-93352) --- Lib/test/test_ast.py | 27 +++++++++++++++++++++++++++ Python/ast.c | 26 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 03d9b31..896eb5b 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -335,6 +335,33 @@ class AST_Tests(unittest.TestCase): for snippet in snippets_to_validate: tree = ast.parse(snippet) compile(tree, '', 'exec') + + def test_invalid_position_information(self): + invalid_linenos = [ + (10, 1), (-10, -11), (10, -11), (-5, -2), (-5, 1) + ] + + for lineno, end_lineno in invalid_linenos: + with self.subTest(f"Check invalid linenos {lineno}:{end_lineno}"): + snippet = "a = 1" + tree = ast.parse(snippet) + tree.body[0].lineno = lineno + tree.body[0].end_lineno = end_lineno + with self.assertRaises(ValueError): + compile(tree, '', 'exec') + + invalid_col_offsets = [ + (10, 1), (-10, -11), (10, -11), (-5, -2), (-5, 1) + ] + for col_offset, end_col_offset in invalid_col_offsets: + with self.subTest(f"Check invalid col_offset {col_offset}:{end_col_offset}"): + snippet = "a = 1" + tree = ast.parse(snippet) + tree.body[0].col_offset = col_offset + tree.body[0].end_col_offset = end_col_offset + with self.assertRaises(ValueError): + compile(tree, '', 'exec') + def test_slice(self): slc = ast.parse("x[::]").body[0].value.slice diff --git a/Python/ast.c b/Python/ast.c index 607281e..0885fe7 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -22,6 +22,27 @@ static int validate_stmt(struct validator *, stmt_ty); static int validate_expr(struct validator *, expr_ty, expr_context_ty); static int validate_pattern(struct validator *, pattern_ty, int); +#define VALIDATE_POSITIONS(node) \ + if (node->lineno > node->end_lineno) { \ + PyErr_Format(PyExc_ValueError, \ + "line %d-%d is not a valid range", \ + node->lineno, node->end_lineno); \ + return 0; \ + } \ + if ((node->lineno < 0 && node->end_lineno != node->lineno) || \ + (node->col_offset < 0 && node->col_offset != node->end_col_offset)) { \ + PyErr_Format(PyExc_ValueError, \ + "line %d-%d, column %d-%d is not a valid range", \ + node->lineno, node->end_lineno, node->col_offset, node->end_col_offset); \ + return 0; \ + } \ + if (node->lineno == node->end_lineno && node->col_offset > node->end_col_offset) { \ + PyErr_Format(PyExc_ValueError, \ + "line %d, column %d-%d is not a valid range", \ + node->lineno, node->col_offset, node->end_col_offset); \ + return 0; \ + } + static int validate_name(PyObject *name) { @@ -75,6 +96,7 @@ validate_args(struct validator *state, asdl_arg_seq *args) Py_ssize_t i; for (i = 0; i < asdl_seq_LEN(args); i++) { arg_ty arg = asdl_seq_GET(args, i); + VALIDATE_POSITIONS(arg); if (arg->annotation && !validate_expr(state, arg->annotation, Load)) return 0; } @@ -183,6 +205,7 @@ validate_constant(struct validator *state, PyObject *value) static int validate_expr(struct validator *state, expr_ty exp, expr_context_ty ctx) { + VALIDATE_POSITIONS(exp); int ret = -1; if (++state->recursion_depth > state->recursion_limit) { PyErr_SetString(PyExc_RecursionError, @@ -505,6 +528,7 @@ validate_capture(PyObject *name) static int validate_pattern(struct validator *state, pattern_ty p, int star_ok) { + VALIDATE_POSITIONS(p); int ret = -1; if (++state->recursion_depth > state->recursion_limit) { PyErr_SetString(PyExc_RecursionError, @@ -674,6 +698,7 @@ validate_body(struct validator *state, asdl_stmt_seq *body, const char *owner) static int validate_stmt(struct validator *state, stmt_ty stmt) { + VALIDATE_POSITIONS(stmt); int ret = -1; Py_ssize_t i; if (++state->recursion_depth > state->recursion_limit) { @@ -807,6 +832,7 @@ validate_stmt(struct validator *state, stmt_ty stmt) } for (i = 0; i < asdl_seq_LEN(stmt->v.Try.handlers); i++) { excepthandler_ty handler = asdl_seq_GET(stmt->v.Try.handlers, i); + VALIDATE_POSITIONS(handler); if ((handler->v.ExceptHandler.type && !validate_expr(state, handler->v.ExceptHandler.type, Load)) || !validate_body(state, handler->v.ExceptHandler.body, "ExceptHandler")) -- cgit v0.12