more fuzzing, recursion, prevent including block devices, utf8 fix
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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(_) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user