From e3ceb5881a793c0924a4e3f03e6e2238906e7731 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 23 Aug 2024 03:39:12 +0300 Subject: [PATCH] parser,ast: protect against more overflows/panics, by forcing early returns on deeply nested expressions and scopes (#22098) --- vlib/v/ast/str.v | 17 +++++++++++++++++ vlib/v/parser/if_match.v | 1 + vlib/v/parser/parser.v | 18 ++++++++++++++++++ ...damsa_too_many_nested_exprs_vfmt_off.vv.txt | 1 + 4 files changed, 37 insertions(+) create mode 100644 vlib/v/parser/testdata/silent/radamsa_too_many_nested_exprs_vfmt_off.vv.txt diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 02fc0739b..1b1063cc0 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -1,10 +1,12 @@ // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. +@[has_globals] module ast import v.util import strings +import sync.stdatomic // get_name returns the real name for the function declaration pub fn (f &FnDecl) get_name() string { @@ -388,8 +390,23 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) { return res.join(''), needs_braces } +__global nested_expr_str_calls = i64(0) +// too big values, risk stack overflow in `${expr}` (which uses `expr.str()`) calls +const max_nested_expr_str_calls = 300 + // string representation of expr pub fn (x Expr) str() string { + str_calls := stdatomic.add_i64(&nested_expr_str_calls, 1) + if str_calls > ast.max_nested_expr_str_calls { + $if panic_on_deeply_nested_expr_str_calls ? { + eprintln('${@LOCATION}: too many nested Expr.str() calls: ${str_calls}, expr type: ${x.type_name()}') + exit(1) + } + return '{expression too deep}' + } + defer { + stdatomic.sub_i64(&nested_expr_str_calls, 1) + } match x { AnonFn { return 'anon_fn' diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index 58f66c6d5..01decb7f5 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -164,6 +164,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { p.inside_if = false p.inside_comptime_if = false if p.opened_scopes > p.max_opened_scopes { + p.should_abort = true p.error('too many nested conditionals, scopes: ${p.opened_scopes}') return ast.IfExpr{} } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index fe69dc1c1..24642879a 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -549,7 +549,9 @@ fn (p &Parser) is_array_type() bool { fn (mut p Parser) open_scope() { if p.opened_scopes > p.max_opened_scopes { + p.should_abort = true p.error('nested opened scopes limit reached: ${p.max_opened_scopes}') + return } p.scope = &ast.Scope{ parent: p.scope @@ -926,7 +928,23 @@ fn (mut p Parser) eat_comments(cfg EatCommentsConfig) []ast.Comment { return comments } +fn (mut p Parser) goto_eof() { + for p.tok.kind != .eof { + p.next() + } +} + fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { + // ensure that possible parser aborts, are handled as early as possible (on the *next* processed statement): + if p.should_abort { + abort_pos := p.tok.pos() + p.goto_eof() + return ast.NodeError{ + idx: 0 + pos: abort_pos + } + } + p.trace_parser('stmt(${is_top_level})') p.is_stmt_ident = p.tok.kind == .name match p.tok.kind { diff --git a/vlib/v/parser/testdata/silent/radamsa_too_many_nested_exprs_vfmt_off.vv.txt b/vlib/v/parser/testdata/silent/radamsa_too_many_nested_exprs_vfmt_off.vv.txt new file mode 100644 index 000000000..e5e12b408 --- /dev/null +++ b/vlib/v/parser/testdata/silent/radamsa_too_many_nested_exprs_vfmt_off.vv.txt @@ -0,0 +1 @@ +----------n----------------------------------------------------------------------------------m-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------M'Hellprintln('Hel-o, World!') -- 2.39.5