mirror of
https://github.com/RustPython/Parser.git
synced 2025-07-22 12:35:22 +00:00
fix: forbid stores to the __debug__ builtin #3203
According to [the docs][1], `__debug__` is treated as a constant by the interpreter. This patch adds some checks to bring RustPython's behaviour in line with cpython's. [1]: https://docs.python.org/3/library/constants.html#built-in-constants
This commit is contained in:
parent
d0c436949e
commit
be92bc5243
1 changed files with 60 additions and 28 deletions
|
@ -22,6 +22,7 @@ use std::borrow::Cow;
|
|||
|
||||
type CompileResult<T> = Result<T, CompileError>;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
enum NameUsage {
|
||||
Load,
|
||||
Store,
|
||||
|
@ -51,6 +52,13 @@ impl CallType {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_forbidden_name(name: &str) -> bool {
|
||||
// See https://docs.python.org/3/library/constants.html#built-in-constants
|
||||
const BUILTIN_CONSTANTS: &[&str] = &["__debug__"];
|
||||
|
||||
BUILTIN_CONSTANTS.contains(&name)
|
||||
}
|
||||
|
||||
/// Main structure holding the state of compilation.
|
||||
struct Compiler {
|
||||
code_stack: Vec<CodeInfo>,
|
||||
|
@ -375,26 +383,39 @@ impl Compiler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn load_name(&mut self, name: &str) {
|
||||
fn load_name(&mut self, name: &str) -> CompileResult<()> {
|
||||
self.compile_name(name, NameUsage::Load)
|
||||
}
|
||||
|
||||
fn store_name(&mut self, name: &str) -> CompileResult<()> {
|
||||
if name.eq("__debug__") {
|
||||
return Err(self.error(CompileErrorType::SyntaxError(
|
||||
"cannot assign to __debug__".to_owned(),
|
||||
)));
|
||||
}
|
||||
self.compile_name(name, NameUsage::Store);
|
||||
Ok(())
|
||||
self.compile_name(name, NameUsage::Store)
|
||||
}
|
||||
|
||||
fn mangle<'a>(&self, name: &'a str) -> Cow<'a, str> {
|
||||
symboltable::mangle_name(self.class_name.as_deref(), name)
|
||||
}
|
||||
|
||||
fn compile_name(&mut self, name: &str, usage: NameUsage) {
|
||||
fn check_forbidden_name(&self, name: &str, usage: NameUsage) -> CompileResult<()> {
|
||||
if usage == NameUsage::Store && is_forbidden_name(name) {
|
||||
return Err(self.error(CompileErrorType::SyntaxError(format!(
|
||||
"cannot assign to {}",
|
||||
name
|
||||
))));
|
||||
}
|
||||
if usage == NameUsage::Delete && is_forbidden_name(name) {
|
||||
return Err(self.error(CompileErrorType::SyntaxError(format!(
|
||||
"cannot delete {}",
|
||||
name
|
||||
))));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_name(&mut self, name: &str, usage: NameUsage) -> CompileResult<()> {
|
||||
let name = self.mangle(name);
|
||||
|
||||
self.check_forbidden_name(&name, usage)?;
|
||||
|
||||
let symbol_table = self.symbol_table_stack.last().unwrap();
|
||||
let symbol = symbol_table.lookup(name.as_ref()).expect(
|
||||
"The symbol must be present in the symbol table, even when it is undefined in python.",
|
||||
|
@ -461,6 +482,8 @@ impl Compiler {
|
|||
},
|
||||
};
|
||||
self.emit(op(idx as u32));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_statement(&mut self, statement: &ast::Stmt) -> CompileResult<()> {
|
||||
|
@ -493,9 +516,9 @@ impl Compiler {
|
|||
let idx = self.name(part);
|
||||
self.emit(Instruction::LoadAttr { idx });
|
||||
}
|
||||
self.store_name(alias)?;
|
||||
self.store_name(alias)?
|
||||
} else {
|
||||
self.store_name(name.name.split('.').next().unwrap())?;
|
||||
self.store_name(name.name.split('.').next().unwrap())?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -551,9 +574,9 @@ impl Compiler {
|
|||
|
||||
// Store module under proper name:
|
||||
if let Some(alias) = &name.asname {
|
||||
self.store_name(alias)?;
|
||||
self.store_name(alias)?
|
||||
} else {
|
||||
self.store_name(&name.name)?;
|
||||
self.store_name(&name.name)?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,10 +795,9 @@ impl Compiler {
|
|||
|
||||
fn compile_delete(&mut self, expression: &ast::Expr) -> CompileResult<()> {
|
||||
match &expression.node {
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
self.compile_name(id, NameUsage::Delete);
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } => self.compile_name(id, NameUsage::Delete)?,
|
||||
ast::ExprKind::Attribute { value, attr, .. } => {
|
||||
self.check_forbidden_name(attr, NameUsage::Delete)?;
|
||||
self.compile_expression(value)?;
|
||||
let idx = self.name(attr);
|
||||
self.emit(Instruction::DeleteAttr { idx });
|
||||
|
@ -852,6 +874,12 @@ impl Compiler {
|
|||
.chain(&args.args)
|
||||
.chain(&args.kwonlyargs);
|
||||
for name in args_iter {
|
||||
if Compiler::is_forbidden_arg_name(&name.node.arg) {
|
||||
return Err(self.error(CompileErrorType::SyntaxError(format!(
|
||||
"cannot assign to {}",
|
||||
&name.node.arg
|
||||
))));
|
||||
}
|
||||
self.varname(&name.node.arg);
|
||||
}
|
||||
|
||||
|
@ -935,7 +963,7 @@ impl Compiler {
|
|||
|
||||
// We have a match, store in name (except x as y)
|
||||
if let Some(alias) = name {
|
||||
self.store_name(alias)?;
|
||||
self.store_name(alias)?
|
||||
} else {
|
||||
// Drop exception from top of stack:
|
||||
self.emit(Instruction::Pop);
|
||||
|
@ -992,6 +1020,10 @@ impl Compiler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn is_forbidden_arg_name(name: &str) -> bool {
|
||||
is_forbidden_name(name)
|
||||
}
|
||||
|
||||
fn compile_function_def(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
@ -1107,9 +1139,7 @@ impl Compiler {
|
|||
|
||||
self.apply_decorators(decorator_list);
|
||||
|
||||
self.store_name(name)?;
|
||||
|
||||
Ok(())
|
||||
self.store_name(name)
|
||||
}
|
||||
|
||||
fn build_closure(&mut self, code: &CodeObject) -> bool {
|
||||
|
@ -1268,8 +1298,7 @@ impl Compiler {
|
|||
|
||||
self.apply_decorators(decorator_list);
|
||||
|
||||
self.store_name(name)?;
|
||||
Ok(())
|
||||
self.store_name(name)
|
||||
}
|
||||
|
||||
fn load_docstring(&mut self, doc_str: Option<String>) {
|
||||
|
@ -1553,15 +1582,14 @@ impl Compiler {
|
|||
|
||||
fn compile_store(&mut self, target: &ast::Expr) -> CompileResult<()> {
|
||||
match &target.node {
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
self.store_name(id)?;
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } => self.store_name(id)?,
|
||||
ast::ExprKind::Subscript { value, slice, .. } => {
|
||||
self.compile_expression(value)?;
|
||||
self.compile_expression(slice)?;
|
||||
self.emit(Instruction::StoreSubscript);
|
||||
}
|
||||
ast::ExprKind::Attribute { value, attr, .. } => {
|
||||
self.check_forbidden_name(attr, NameUsage::Store)?;
|
||||
self.compile_expression(value)?;
|
||||
let idx = self.name(attr);
|
||||
self.emit(Instruction::StoreAttr { idx });
|
||||
|
@ -1950,9 +1978,7 @@ impl Compiler {
|
|||
conversion: compile_conversion_flag(*conversion),
|
||||
});
|
||||
}
|
||||
Name { id, .. } => {
|
||||
self.load_name(id);
|
||||
}
|
||||
Name { id, .. } => self.load_name(id)?,
|
||||
Lambda { args, body } => {
|
||||
let prev_ctx = self.ctx;
|
||||
self.ctx = CompileContext {
|
||||
|
@ -2153,6 +2179,12 @@ impl Compiler {
|
|||
let (size, unpack) = self.gather_elements(additional_positional, args)?;
|
||||
let has_double_star = keywords.iter().any(|k| k.node.arg.is_none());
|
||||
|
||||
for keyword in keywords {
|
||||
if let Some(name) = &keyword.node.arg {
|
||||
self.check_forbidden_name(name, NameUsage::Store)?;
|
||||
}
|
||||
}
|
||||
|
||||
let call = if unpack || has_double_star {
|
||||
// Create a tuple with positional args:
|
||||
self.emit(Instruction::BuildTuple { size, unpack });
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue