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 std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
use crate::tokenizer::{
|
use crate::tokenizer::{
|
||||||
Token,
|
Loc, Token,
|
||||||
TokenType::{self, Identifier},
|
TokenType::{self, Identifier},
|
||||||
ZernError, error,
|
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);
|
pub static NEXT_EXPR_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -479,17 +513,12 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn expression(&mut self) -> Result<Expr, ZernError> {
|
fn expression(&mut self) -> Result<Expr, ZernError> {
|
||||||
self.depth += 1;
|
recursion_guard!(self);
|
||||||
if self.depth > 200 {
|
self.or_and()
|
||||||
return error!(self.previous().loc, "maximum expression depth reached");
|
|
||||||
}
|
|
||||||
|
|
||||||
let out = self.or_and();
|
|
||||||
self.depth -= 1;
|
|
||||||
out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn or_and(&mut self) -> Result<Expr, ZernError> {
|
fn or_and(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
let mut expr = self.equality()?;
|
let mut expr = self.equality()?;
|
||||||
|
|
||||||
while self.match_token(&[TokenType::LogicalOr, TokenType::LogicalAnd]) {
|
while self.match_token(&[TokenType::LogicalOr, TokenType::LogicalAnd]) {
|
||||||
@@ -506,6 +535,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn equality(&mut self) -> Result<Expr, ZernError> {
|
fn equality(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
let mut expr = self.comparison()?;
|
let mut expr = self.comparison()?;
|
||||||
|
|
||||||
while self.match_token(&[TokenType::DoubleEqual, TokenType::NotEqual]) {
|
while self.match_token(&[TokenType::DoubleEqual, TokenType::NotEqual]) {
|
||||||
@@ -522,6 +552,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn comparison(&mut self) -> Result<Expr, ZernError> {
|
fn comparison(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
let mut expr = self.term()?;
|
let mut expr = self.term()?;
|
||||||
|
|
||||||
while self.match_token(&[
|
while self.match_token(&[
|
||||||
@@ -543,6 +574,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn term(&mut self) -> Result<Expr, ZernError> {
|
fn term(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
let mut expr = self.factor()?;
|
let mut expr = self.factor()?;
|
||||||
|
|
||||||
while self.match_token(&[
|
while self.match_token(&[
|
||||||
@@ -565,6 +597,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn factor(&mut self) -> Result<Expr, ZernError> {
|
fn factor(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
let mut expr = self.cast()?;
|
let mut expr = self.cast()?;
|
||||||
|
|
||||||
while self.match_token(&[
|
while self.match_token(&[
|
||||||
@@ -587,6 +620,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cast(&mut self) -> Result<Expr, ZernError> {
|
fn cast(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
let mut expr = self.unary()?;
|
let mut expr = self.unary()?;
|
||||||
|
|
||||||
while self.match_token(&[TokenType::KeywordAs]) {
|
while self.match_token(&[TokenType::KeywordAs]) {
|
||||||
@@ -601,6 +635,8 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn unary(&mut self) -> Result<Expr, ZernError> {
|
fn unary(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
|
|
||||||
if self.match_token(&[TokenType::Xor]) {
|
if self.match_token(&[TokenType::Xor]) {
|
||||||
let op = self.previous().clone();
|
let op = self.previous().clone();
|
||||||
let right = self.unary()?;
|
let right = self.unary()?;
|
||||||
@@ -622,6 +658,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self) -> Result<Expr, ZernError> {
|
fn call(&mut self) -> Result<Expr, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
let mut expr = self.primary()?;
|
let mut expr = self.primary()?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::{
|
|||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
fmt, fs,
|
fmt, fs,
|
||||||
|
os::unix::fs::FileTypeExt,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -473,6 +474,19 @@ impl<'a> Tokenizer<'a> {
|
|||||||
return Ok(());
|
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) {
|
let source = match fs::read_to_string(&canonical) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Expr, ExprKind, Params, Stmt},
|
parser::{Expr, ExprKind, Params, ScopeCall, Stmt, recursion_guard},
|
||||||
symbol_table::{FnParams, SymbolTable},
|
symbol_table::{FnParams, SymbolTable},
|
||||||
tokenizer::{TokenType, ZernError, error},
|
tokenizer::{Loc, TokenType, ZernError, error},
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! expect_type {
|
macro_rules! expect_type {
|
||||||
@@ -73,6 +73,7 @@ pub struct TypeChecker<'a> {
|
|||||||
symbol_table: &'a SymbolTable,
|
symbol_table: &'a SymbolTable,
|
||||||
pub expr_types: HashMap<usize, String>,
|
pub expr_types: HashMap<usize, String>,
|
||||||
current_function_return_type: String,
|
current_function_return_type: String,
|
||||||
|
depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TypeChecker<'a> {
|
impl<'a> TypeChecker<'a> {
|
||||||
@@ -81,6 +82,7 @@ impl<'a> TypeChecker<'a> {
|
|||||||
symbol_table,
|
symbol_table,
|
||||||
expr_types: HashMap::new(),
|
expr_types: HashMap::new(),
|
||||||
current_function_return_type: String::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> {
|
pub fn typecheck_expr(&mut self, env: &mut Env, expr: &Expr) -> Result<String, ZernError> {
|
||||||
|
recursion_guard!(self);
|
||||||
|
|
||||||
let expr_type = match &expr.kind {
|
let expr_type = match &expr.kind {
|
||||||
ExprKind::Binary { left, op, right } => {
|
ExprKind::Binary { left, op, right } => {
|
||||||
let left_type = self.typecheck_expr(env, left)?;
|
let left_type = self.typecheck_expr(env, left)?;
|
||||||
@@ -685,7 +689,7 @@ impl<'a> TypeChecker<'a> {
|
|||||||
if let Some(args) = inner {
|
if let Some(args) = inner {
|
||||||
return args.iter().all(|arg| self.is_valid_type_name(arg));
|
return args.iter().all(|arg| self.is_valid_type_name(arg));
|
||||||
}
|
}
|
||||||
return false;
|
unreachable!();
|
||||||
}
|
}
|
||||||
if BUILTIN_TYPES.contains(&name) {
|
if BUILTIN_TYPES.contains(&name) {
|
||||||
return true;
|
return true;
|
||||||
@@ -700,7 +704,8 @@ impl<'a> TypeChecker<'a> {
|
|||||||
fn parse_generic_type(s: &str) -> (&str, Option<Vec<&str>>) {
|
fn parse_generic_type(s: &str) -> (&str, Option<Vec<&str>>) {
|
||||||
if let Some(lt_pos) = s.find('<') {
|
if let Some(lt_pos) = s.find('<') {
|
||||||
let base = &s[..lt_pos];
|
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 args = Vec::new();
|
||||||
let mut depth = 0;
|
let mut depth = 0;
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user