more fuzzing, recursion, prevent including block devices, utf8 fix

This commit is contained in:
2026-07-17 14:21:29 +02:00
parent 1471c229c2
commit 45171f688c
3 changed files with 69 additions and 13 deletions

View File

@@ -1,7 +1,7 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::tokenizer::{
Token,
Loc, Token,
TokenType::{self, Identifier},
ZernError, error,
};
@@ -79,6 +79,40 @@ pub enum Stmt {
},
}
// https://stackoverflow.com/a/29963675
pub struct ScopeCall<F: FnMut()> {
pub c: F,
}
impl<F: FnMut()> Drop for ScopeCall<F> {
fn drop(&mut self) {
(self.c)();
}
}
macro_rules! recursion_guard {
($self:ident) => {
$self.depth += 1;
if $self.depth > 200 {
return error!(
Loc {
filename: "<unknown>".into(),
line: 0,
column: 0,
},
"maximum expression depth reached"
);
}
let self_ptr = $self as *mut Self;
let _scope_call = ScopeCall {
c: || unsafe {
(*self_ptr).depth -= 1;
},
};
};
}
pub(crate) use recursion_guard;
pub static NEXT_EXPR_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, Clone)]
@@ -479,17 +513,12 @@ impl Parser {
}
fn expression(&mut self) -> Result<Expr, ZernError> {
self.depth += 1;
if self.depth > 200 {
return error!(self.previous().loc, "maximum expression depth reached");
}
let out = self.or_and();
self.depth -= 1;
out
recursion_guard!(self);
self.or_and()
}
fn or_and(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
let mut expr = self.equality()?;
while self.match_token(&[TokenType::LogicalOr, TokenType::LogicalAnd]) {
@@ -506,6 +535,7 @@ impl Parser {
}
fn equality(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
let mut expr = self.comparison()?;
while self.match_token(&[TokenType::DoubleEqual, TokenType::NotEqual]) {
@@ -522,6 +552,7 @@ impl Parser {
}
fn comparison(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
let mut expr = self.term()?;
while self.match_token(&[
@@ -543,6 +574,7 @@ impl Parser {
}
fn term(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
let mut expr = self.factor()?;
while self.match_token(&[
@@ -565,6 +597,7 @@ impl Parser {
}
fn factor(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
let mut expr = self.cast()?;
while self.match_token(&[
@@ -587,6 +620,7 @@ impl Parser {
}
fn cast(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
let mut expr = self.unary()?;
while self.match_token(&[TokenType::KeywordAs]) {
@@ -601,6 +635,8 @@ impl Parser {
}
fn unary(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
if self.match_token(&[TokenType::Xor]) {
let op = self.previous().clone();
let right = self.unary()?;
@@ -622,6 +658,7 @@ impl Parser {
}
fn call(&mut self) -> Result<Expr, ZernError> {
recursion_guard!(self);
let mut expr = self.primary()?;
loop {

View File

@@ -2,6 +2,7 @@ use std::{
cmp::Ordering,
collections::HashSet,
fmt, fs,
os::unix::fs::FileTypeExt,
path::{Path, PathBuf},
};
@@ -473,6 +474,19 @@ impl<'a> Tokenizer<'a> {
return Ok(());
}
let meta = match std::fs::metadata(&canonical) {
Ok(x) => x,
Err(_) => {
return error!(self.loc, format!("failed to access {}", path));
}
};
if !meta.file_type().is_file() {
return error!(
self.loc,
format!("refusing to read {} because it is not a regular file", path)
);
}
let source = match fs::read_to_string(&canonical) {
Ok(x) => x,
Err(_) => {

View File

@@ -1,9 +1,9 @@
use std::collections::HashMap;
use crate::{
parser::{Expr, ExprKind, Params, Stmt},
parser::{Expr, ExprKind, Params, ScopeCall, Stmt, recursion_guard},
symbol_table::{FnParams, SymbolTable},
tokenizer::{TokenType, ZernError, error},
tokenizer::{Loc, TokenType, ZernError, error},
};
macro_rules! expect_type {
@@ -73,6 +73,7 @@ pub struct TypeChecker<'a> {
symbol_table: &'a SymbolTable,
pub expr_types: HashMap<usize, String>,
current_function_return_type: String,
depth: usize,
}
impl<'a> TypeChecker<'a> {
@@ -81,6 +82,7 @@ impl<'a> TypeChecker<'a> {
symbol_table,
expr_types: HashMap::new(),
current_function_return_type: String::new(),
depth: 0,
}
}
@@ -373,6 +375,8 @@ impl<'a> TypeChecker<'a> {
}
pub fn typecheck_expr(&mut self, env: &mut Env, expr: &Expr) -> Result<String, ZernError> {
recursion_guard!(self);
let expr_type = match &expr.kind {
ExprKind::Binary { left, op, right } => {
let left_type = self.typecheck_expr(env, left)?;
@@ -685,7 +689,7 @@ impl<'a> TypeChecker<'a> {
if let Some(args) = inner {
return args.iter().all(|arg| self.is_valid_type_name(arg));
}
return false;
unreachable!();
}
if BUILTIN_TYPES.contains(&name) {
return true;
@@ -700,7 +704,8 @@ impl<'a> TypeChecker<'a> {
fn parse_generic_type(s: &str) -> (&str, Option<Vec<&str>>) {
if let Some(lt_pos) = s.find('<') {
let base = &s[..lt_pos];
let inner = &s[lt_pos + 1..s.len() - 1];
let close_pos = s.rfind('>').unwrap_or(s.len());
let inner = &s[lt_pos + 1..close_pos];
let mut args = Vec::new();
let mut depth = 0;
let mut start = 0;