Skip to content

Commit ca39363

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

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
@@ -960,6 +960,34 @@ def test_constant_as_name(self):
960960
):
961961
compile(expr, "<test>", "eval")
962962

963+
def test_constant_in_identifier_fields(self):
964+
# gh-85260: an identifier field holding a constant name used to
965+
# crash the compiler
966+
for statement in [
967+
"def x(): pass",
968+
"async def x(): pass",
969+
"class x: pass",
970+
"from a import x",
971+
"from a import b as x",
972+
"from a import b, c, d as x",
973+
"import x",
974+
"import a, b, x",
975+
"try: pass\nexcept A as x: pass",
976+
"try: pass\nexcept A as b: pass\nexcept B as x: pass\n",
977+
]:
978+
for constant in "True", "False", "None":
979+
with self.subTest(statement=statement, constant=constant):
980+
tree = ast.parse(statement)
981+
for node in ast.walk(tree):
982+
for field, value in ast.iter_fields(node):
983+
if value == "x":
984+
setattr(node, field, constant)
985+
with self.assertRaisesRegex(
986+
ValueError,
987+
f"identifier field can't represent "
988+
f"'{constant}' constant"):
989+
compile(tree, "<test>", "exec")
990+
963991
def test_constant_as_unicode_name(self):
964992
constants = [
965993
("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
@@ -704,6 +704,23 @@ _validate_nonempty_seq(asdl_seq *seq, const char *what, const char *owner)
704704
}
705705
#define validate_nonempty_seq(seq, what, owner) _validate_nonempty_seq((asdl_seq*)seq, what, owner)
706706

707+
static int
708+
validate_import_names(asdl_alias_seq *seq, const char *what, const char *owner)
709+
{
710+
if (!validate_nonempty_seq(seq, what, owner)) {
711+
return 0;
712+
}
713+
Py_ssize_t n = asdl_seq_LEN(seq);
714+
for (Py_ssize_t i = 0; i < n; i++) {
715+
alias_ty alias = asdl_seq_GET(seq, i);
716+
if (!validate_name(alias->name) ||
717+
(alias->asname && !validate_name(alias->asname))) {
718+
return 0;
719+
}
720+
}
721+
return 1;
722+
}
723+
707724
static int
708725
validate_assignlist(struct validator *state, asdl_expr_seq *targets, expr_context_ty ctx)
709726
{
@@ -733,6 +750,7 @@ validate_stmt(struct validator *state, stmt_ty stmt)
733750
switch (stmt->kind) {
734751
case FunctionDef_kind:
735752
ret = validate_body(state, stmt->v.FunctionDef.body, "FunctionDef") &&
753+
validate_name(stmt->v.FunctionDef.name) &&
736754
validate_type_params(state, stmt->v.FunctionDef.type_params) &&
737755
validate_arguments(state, stmt->v.FunctionDef.args) &&
738756
validate_exprs(state, stmt->v.FunctionDef.decorator_list, Load, 0) &&
@@ -741,6 +759,7 @@ validate_stmt(struct validator *state, stmt_ty stmt)
741759
break;
742760
case ClassDef_kind:
743761
ret = validate_body(state, stmt->v.ClassDef.body, "ClassDef") &&
762+
validate_name(stmt->v.ClassDef.name) &&
744763
validate_type_params(state, stmt->v.ClassDef.type_params) &&
745764
validate_exprs(state, stmt->v.ClassDef.bases, Load, 0) &&
746765
validate_keywords(state, stmt->v.ClassDef.keywords) &&
@@ -871,6 +890,8 @@ validate_stmt(struct validator *state, stmt_ty stmt)
871890
VALIDATE_POSITIONS(handler);
872891
if ((handler->v.ExceptHandler.type &&
873892
!validate_expr(state, handler->v.ExceptHandler.type, Load)) ||
893+
(handler->v.ExceptHandler.name &&
894+
!validate_name(handler->v.ExceptHandler.name)) ||
874895
!validate_body(state, handler->v.ExceptHandler.body, "ExceptHandler"))
875896
return 0;
876897
}
@@ -909,14 +930,14 @@ validate_stmt(struct validator *state, stmt_ty stmt)
909930
(!stmt->v.Assert.msg || validate_expr(state, 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(struct validator *state, stmt_ty stmt)
929950
break;
930951
case AsyncFunctionDef_kind:
931952
ret = validate_body(state, stmt->v.AsyncFunctionDef.body, "AsyncFunctionDef") &&
953+
validate_name(stmt->v.AsyncFunctionDef.name) &&
932954
validate_type_params(state, stmt->v.AsyncFunctionDef.type_params) &&
933955
validate_arguments(state, stmt->v.AsyncFunctionDef.args) &&
934956
validate_exprs(state, stmt->v.AsyncFunctionDef.decorator_list, Load, 0) &&

0 commit comments

Comments
 (0)