[Python-checkins] bpo-41060: Avoid SEGFAULT when calling GET_INVALID_TARGET in the grammar (GH-21020)

Lysandros Nikolaou webhook-mailer at python.org
Sat Jun 20 22:18:08 EDT 2020


https://github.com/python/cpython/commit/6c4e0bd974f2895d42b63d9d004587e74b286c88
commit: 6c4e0bd974f2895d42b63d9d004587e74b286c88
branch: master
author: Lysandros Nikolaou <lisandrosnik at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-06-21T03:18:01+01:00
summary:

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.

files:
M Grammar/python.gram
M Lib/test/test_syntax.py
M Parser/parser.c
M Parser/pegen.h

diff --git a/Grammar/python.gram b/Grammar/python.gram
index e4abca9388eb0..c5a5dbe1724f3 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 9bb3d9ee44448..812a7df3228bc 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -199,6 +199,10 @@
 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 @@
 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 1531c99f83891..323cd0e0efae3 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 ef095dda49fd7..f407709863c69 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);



More information about the Python-checkins mailing list