From 6c4e0bd974f2895d42b63d9d004587e74b286c88 Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Sun, 21 Jun 2020 05:18:01 +0300 Subject: bpo-41060: Avoid SEGFAULT when calling GET_INVALID_TARGET in the grammar (GH-21020) `GET_INVALID_TARGET` might unexpectedly return `NULL`, which if not caught will cause a SEGFAULT. Therefore, this commit introduces a new inline function `RAISE_SYNTAX_ERROR_INVALID_TARGET` that always checks for `GET_INVALID_TARGET` returning NULL and can be used in the grammar, replacing the long C ternary operation used till now. --- Grammar/python.gram | 23 ++++------------------- Lib/test/test_syntax.py | 8 ++++++++ Parser/parser.c | 8 ++++---- Parser/pegen.h | 25 ++++++++++++++++++++++--- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/Grammar/python.gram b/Grammar/python.gram index e4abca9..c5a5dbe 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -653,9 +653,7 @@ invalid_assignment: | a=expression ':' expression ['=' annotated_rhs] { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "illegal target for annotation") } | (star_targets '=')* a=star_expressions '=' { - RAISE_SYNTAX_ERROR_KNOWN_LOCATION( - GET_INVALID_TARGET(a), - "cannot assign to %s", _PyPegen_get_expr_name(GET_INVALID_TARGET(a))) } + RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) } | (star_targets '=')* a=yield_expr '=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "assignment to yield expression not possible") } | a=star_expressions augassign (yield_expr | star_expressions) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION( @@ -665,12 +663,7 @@ invalid_assignment: )} invalid_del_stmt: | 'del' a=star_expressions { - GET_INVALID_DEL_TARGET(a) != NULL ? - RAISE_SYNTAX_ERROR_KNOWN_LOCATION( - GET_INVALID_DEL_TARGET(a), - "cannot delete %s", _PyPegen_get_expr_name(GET_INVALID_DEL_TARGET(a)) - ) : - RAISE_SYNTAX_ERROR("invalid syntax") } + RAISE_SYNTAX_ERROR_INVALID_TARGET(DEL_TARGETS, a) } invalid_block: | NEWLINE !INDENT { RAISE_INDENTATION_ERROR("expected an indented block") } invalid_comprehension: @@ -695,19 +688,11 @@ invalid_double_type_comments: RAISE_SYNTAX_ERROR("Cannot have two type comments on def") } invalid_with_item: | expression 'as' a=expression { - RAISE_SYNTAX_ERROR_KNOWN_LOCATION( - GET_INVALID_TARGET(a), - "cannot assign to %s", _PyPegen_get_expr_name(GET_INVALID_TARGET(a)) - ) } + RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) } invalid_for_target: | ASYNC? 'for' a=star_expressions { - GET_INVALID_FOR_TARGET(a) != NULL ? - RAISE_SYNTAX_ERROR_KNOWN_LOCATION( - GET_INVALID_FOR_TARGET(a), - "cannot assign to %s", _PyPegen_get_expr_name(GET_INVALID_FOR_TARGET(a)) - ) : - RAISE_SYNTAX_ERROR("invalid syntax") } + RAISE_SYNTAX_ERROR_INVALID_TARGET(FOR_TARGETS, a) } invalid_group: | '(' a=starred_expression ')' { diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 9bb3d9e..812a7df 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -199,6 +199,10 @@ SyntaxError: cannot assign to operator Traceback (most recent call last): SyntaxError: invalid syntax +>>> for a, b +Traceback (most recent call last): +SyntaxError: invalid syntax + >>> with a as b(): pass Traceback (most recent call last): SyntaxError: cannot assign to function call @@ -223,6 +227,10 @@ SyntaxError: cannot assign to function call Traceback (most recent call last): SyntaxError: cannot assign to function call +>>> with a as b +Traceback (most recent call last): +SyntaxError: invalid syntax + >>> p = p = Traceback (most recent call last): SyntaxError: invalid syntax diff --git a/Parser/parser.c b/Parser/parser.c index 1531c99..323cd0e 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -14818,7 +14818,7 @@ invalid_assignment_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='")); - _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( GET_INVALID_TARGET ( a ) , "cannot assign to %s" , _PyPegen_get_expr_name ( GET_INVALID_TARGET ( a ) ) ); + _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( STAR_TARGETS , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -14922,7 +14922,7 @@ invalid_del_stmt_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ invalid_del_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'del' star_expressions")); - _res = GET_INVALID_DEL_TARGET ( a ) != NULL ? RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( GET_INVALID_DEL_TARGET ( a ) , "cannot delete %s" , _PyPegen_get_expr_name ( GET_INVALID_DEL_TARGET ( a ) ) ) : RAISE_SYNTAX_ERROR ( "invalid syntax" ); + _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( DEL_TARGETS , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -15379,7 +15379,7 @@ invalid_with_item_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ invalid_with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' expression")); - _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( GET_INVALID_TARGET ( a ) , "cannot assign to %s" , _PyPegen_get_expr_name ( GET_INVALID_TARGET ( a ) ) ); + _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( STAR_TARGETS , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); @@ -15427,7 +15427,7 @@ invalid_for_target_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ invalid_for_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "ASYNC? 'for' star_expressions")); - _res = GET_INVALID_FOR_TARGET ( a ) != NULL ? RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( GET_INVALID_FOR_TARGET ( a ) , "cannot assign to %s" , _PyPegen_get_expr_name ( GET_INVALID_FOR_TARGET ( a ) ) ) : RAISE_SYNTAX_ERROR ( "invalid syntax" ); + _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( FOR_TARGETS , a ); if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; D(p->level--); diff --git a/Parser/pegen.h b/Parser/pegen.h index ef095dd..f407709 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -269,9 +269,28 @@ typedef enum { FOR_TARGETS } TARGETS_TYPE; expr_ty _PyPegen_get_invalid_target(expr_ty e, TARGETS_TYPE targets_type); -#define GET_INVALID_TARGET(e) (expr_ty)CHECK(_PyPegen_get_invalid_target(e, STAR_TARGETS)) -#define GET_INVALID_DEL_TARGET(e) (expr_ty)CHECK_NULL_ALLOWED(_PyPegen_get_invalid_target(e, DEL_TARGETS)) -#define GET_INVALID_FOR_TARGET(e) (expr_ty)CHECK_NULL_ALLOWED(_PyPegen_get_invalid_target(e, FOR_TARGETS)) +#define RAISE_SYNTAX_ERROR_INVALID_TARGET(type, e) _RAISE_SYNTAX_ERROR_INVALID_TARGET(p, type, e) + +Py_LOCAL_INLINE(void *) +_RAISE_SYNTAX_ERROR_INVALID_TARGET(Parser *p, TARGETS_TYPE type, void *e) +{ + expr_ty invalid_target = CHECK_NULL_ALLOWED(_PyPegen_get_invalid_target(e, type)); + if (invalid_target != NULL) { + const char *msg; + if (type == STAR_TARGETS || type == FOR_TARGETS) { + msg = "cannot assign to %s"; + } + else { + msg = "cannot delete %s"; + } + return RAISE_SYNTAX_ERROR_KNOWN_LOCATION( + invalid_target, + msg, + _PyPegen_get_expr_name(invalid_target) + ); + } + return RAISE_SYNTAX_ERROR("invalid syntax"); +} void *_PyPegen_arguments_parsing_error(Parser *, expr_ty); void *_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args); -- cgit v0.12