Skip to content

Commit 14d1280

Browse files
[3.14] gh-85260: Extend the AST Validator to validate all identifiers (GH-21069) (GH-155651)
(cherry picked from commit 47e2175) Co-authored-by: Batuhan Taskaya <isidentical@gmail.com>
1 parent af0e7a6 commit 14d1280

3 files changed

Lines changed: 56 additions & 2 deletions

File tree

Lib/test/test_ast/test_ast.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,34 @@ def test_constant_as_name(self):
948948
with self.assertRaisesRegex(ValueError, f"identifier field can't represent '{constant}' constant"):
949949
compile(expr, "<test>", "eval")
950950

951+
def test_constant_in_identifier_fields(self):
952+
# gh-85260: an identifier field holding a constant name used to
953+
# crash the compiler
954+
for statement in [
955+
"def x(): pass",
956+
"async def x(): pass",
957+
"class x: pass",
958+
"from a import x",
959+
"from a import b as x",
960+
"from a import b, c, d as x",
961+
"import x",
962+
"import a, b, x",
963+
"try: pass\nexcept A as x: pass",
964+
"try: pass\nexcept A as b: pass\nexcept B as x: pass\n",
965+
]:
966+
for constant in "True", "False", "None":
967+
with self.subTest(statement=statement, constant=constant):
968+
tree = ast.parse(statement)
969+
for node in ast.walk(tree):
970+
for field, value in ast.iter_fields(node):
971+
if value == "x":
972+
setattr(node, field, constant)
973+
with self.assertRaisesRegex(
974+
ValueError,
975+
f"identifier field can't represent "
976+
f"'{constant}' constant"):
977+
compile(tree, "<test>", "exec")
978+
951979
def test_constant_as_unicode_name(self):
952980
constants = [
953981
("True", b"Tru\xe1\xb5\x89"),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:func:`compile` now raises :exc:`ValueError` instead of crashing on a debug
2+
build if an identifier field of an AST node (such as the name of a function,
3+
a class, an imported module or a caught exception) is ``"None"``, ``"True"``
4+
or ``"False"``.

Python/ast.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,23 @@ _validate_nonempty_seq(asdl_seq *seq, const char *what, const char *owner)
708708
}
709709
#define validate_nonempty_seq(seq, what, owner) _validate_nonempty_seq((asdl_seq*)seq, what, owner)
710710

711+
static int
712+
validate_import_names(asdl_alias_seq *seq, const char *what, const char *owner)
713+
{
714+
if (!validate_nonempty_seq(seq, what, owner)) {
715+
return 0;
716+
}
717+
Py_ssize_t n = asdl_seq_LEN(seq);
718+
for (Py_ssize_t i = 0; i < n; i++) {
719+
alias_ty alias = asdl_seq_GET(seq, i);
720+
if (!validate_name(alias->name) ||
721+
(alias->asname && !validate_name(alias->asname))) {
722+
return 0;
723+
}
724+
}
725+
return 1;
726+
}
727+
711728
static int
712729
validate_assignlist(asdl_expr_seq *targets, expr_context_ty ctx)
713730
{
@@ -733,6 +750,7 @@ validate_stmt(stmt_ty stmt)
733750
switch (stmt->kind) {
734751
case FunctionDef_kind:
735752
ret = validate_body(stmt->v.FunctionDef.body, "FunctionDef") &&
753+
validate_name(stmt->v.FunctionDef.name) &&
736754
validate_type_params(stmt->v.FunctionDef.type_params) &&
737755
validate_arguments(stmt->v.FunctionDef.args) &&
738756
validate_exprs(stmt->v.FunctionDef.decorator_list, Load, 0) &&
@@ -741,6 +759,7 @@ validate_stmt(stmt_ty stmt)
741759
break;
742760
case ClassDef_kind:
743761
ret = validate_body(stmt->v.ClassDef.body, "ClassDef") &&
762+
validate_name(stmt->v.ClassDef.name) &&
744763
validate_type_params(stmt->v.ClassDef.type_params) &&
745764
validate_exprs(stmt->v.ClassDef.bases, Load, 0) &&
746765
validate_keywords(stmt->v.ClassDef.keywords) &&
@@ -871,6 +890,8 @@ validate_stmt(stmt_ty stmt)
871890
VALIDATE_POSITIONS(handler);
872891
if ((handler->v.ExceptHandler.type &&
873892
!validate_expr(handler->v.ExceptHandler.type, Load)) ||
893+
(handler->v.ExceptHandler.name &&
894+
!validate_name(handler->v.ExceptHandler.name)) ||
874895
!validate_body(handler->v.ExceptHandler.body, "ExceptHandler"))
875896
return 0;
876897
}
@@ -909,14 +930,14 @@ validate_stmt(stmt_ty stmt)
909930
(!stmt->v.Assert.msg || validate_expr(stmt->v.Assert.msg, Load));
910931
break;
911932
case Import_kind:
912-
ret = validate_nonempty_seq(stmt->v.Import.names, "names", "Import");
933+
ret = validate_import_names(stmt->v.Import.names, "names", "Import");
913934
break;
914935
case ImportFrom_kind:
915936
if (stmt->v.ImportFrom.level < 0) {
916937
PyErr_SetString(PyExc_ValueError, "Negative ImportFrom level");
917938
return 0;
918939
}
919-
ret = validate_nonempty_seq(stmt->v.ImportFrom.names, "names", "ImportFrom");
940+
ret = validate_import_names(stmt->v.ImportFrom.names, "names", "ImportFrom");
920941
break;
921942
case Global_kind:
922943
ret = validate_nonempty_seq(stmt->v.Global.names, "names", "Global");
@@ -929,6 +950,7 @@ validate_stmt(stmt_ty stmt)
929950
break;
930951
case AsyncFunctionDef_kind:
931952
ret = validate_body(stmt->v.AsyncFunctionDef.body, "AsyncFunctionDef") &&
953+
validate_name(stmt->v.AsyncFunctionDef.name) &&
932954
validate_type_params(stmt->v.AsyncFunctionDef.type_params) &&
933955
validate_arguments(stmt->v.AsyncFunctionDef.args) &&
934956
validate_exprs(stmt->v.AsyncFunctionDef.decorator_list, Load, 0) &&

0 commit comments

Comments
 (0)