Skip to content

ast: Out of bounds memory access crash in AST Vititor #8437

@valeneiko

Description

@valeneiko

Summary

After adding some unrelated code to a function in my project oxc::ast::Visit::visit_program started crashing with out-of-bounds memory error:

error: process didn't exit successfully: `target\debug\test-runner.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

Stack Trace

Attaching a debugger to the program I got the following stack trace:

test-runner.exe!oxc_span::atom::Atom::as_str() Line 34 (c:\Users\valja\.cargo\registry\src\index.crates.io-6f17d22bba15001f\oxc_span-0.44.0\src\atom.rs:34)
test-runner.exe!oxc_span::atom::impl$17::eq<ref$<str$>>(oxc_span::atom::Atom * self, ref$<str$> * other) Line 159 (c:\Users\valja\.cargo\registry\src\index.crates.io-6f17d22bba15001f\oxc_span-0.44.0\src\atom.rs:159)
test-runner.exe!oxc_ast::ast::js::Directive::is_use_strict() Line 787 (c:\Users\valja\.cargo\registry\src\index.crates.io-6f17d22bba15001f\oxc_ast-0.44.0\src\ast_impl\js.rs:787)
test-runner.exe!core::ops::function::FnMut::call_mut<bool (*)(ref$<oxc_ast::ast::js::Directive>),tuple$<ref$<oxc_ast::ast::js::Directive>>>(bool(*)(oxc_ast::ast::js::Directive *) *, oxc_ast::ast::js::Directive *) Line 166 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:166)
test-runner.exe!core::slice::iter::impl$182::any<oxc_ast::ast::js::Directive,bool (*)(ref$<oxc_ast::ast::js::Directive>)>(core::slice::iter::Iter<oxc_ast::ast::js::Directive> * self, bool(*)(oxc_ast::ast::js::Directive *) f) Line 285 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\slice\iter\macros.rs:285)
test-runner.exe!oxc_ast::ast::js::Program::has_use_strict_directive() Line 20 (c:\Users\valja\.cargo\registry\src\index.crates.io-6f17d22bba15001f\oxc_ast-0.44.0\src\ast_impl\js.rs:20)
test-runner.exe!oxc_ast::generated::visit::walk::walk_program<test_runner::TypeVisitorImpl<core::iter::adapters::flatten::FlatMap<core::slice::iter::Iter<alloc::vec::Vec<test_runner::Assertion,alloc::alloc::Global>>,core::slice::iter::Iter<test_runner::Assertion>,test_runner::impl$0::run::closure_env$1>>>(test_runner::TypeVisitorImpl<core::iter::adapters::flatten::FlatMap<core::slice::iter::Iter<alloc::vec::Vec<test_runner::Assertion,alloc::alloc::Global>>,core::slice::iter::Iter<test_runner::Assertion>,test_runner::impl$0::run::closure_env$1>> * visitor, oxc_ast::ast::js::Program * it) Line 1347 (c:\Users\valja\.cargo\registry\src\index.crates.io-6f17d22bba15001f\oxc_ast-0.44.0\src\generated\visit.rs:1347)
test-runner.exe!oxc_ast::generated::visit::Visit::visit_program<test_runner::TypeVisitorImpl<core::iter::adapters::flatten::FlatMap<core::slice::iter::Iter<alloc::vec::Vec<test_runner::Assertion,alloc::alloc::Global>>,core::slice::iter::Iter<test_runner::Assertion>,test_runner::impl$0::run::closure_env$1>>>(test_runner::TypeVisitorImpl<core::iter::adapters::flatten::FlatMap<core::slice::iter::Iter<alloc::vec::Vec<test_runner::Assertion,alloc::alloc::Global>>,core::slice::iter::Iter<test_runner::Assertion>,test_runner::impl$0::run::closure_env$1>> * self, oxc_ast::ast::js::Program * it) Line 52 (c:\Users\valja\.cargo\registry\src\index.crates.io-6f17d22bba15001f\oxc_ast-0.44.0\src\generated\visit.rs:52)
test-runner.exe!test_runner::TypeVisitor::run() Line 59 (c:\Users\valja\source\rust\rustify\crates\test_runner\src\main.rs:59)
test-runner.exe!test_runner::main() Line 15 (c:\Users\valja\source\rust\rustify\crates\test_runner\src\main.rs:15)
test-runner.exe!core::ops::function::FnOnce::call_once<void (*)(),tuple$<>>(void(*)()) Line 250 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:250)
test-runner.exe!std::sys::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<>>(void(*)() f) Line 160 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\sys\backtrace.rs:160)
test-runner.exe!std::rt::lang_start::closure$0<tuple$<>>(std::rt::lang_start::closure_env$0<tuple$<>> *) Line 195 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:195)
[Inline Frame] test-runner.exe!std::rt::lang_start_internal::closure$1() Line 174 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:174)
[Inline Frame] test-runner.exe!std::panicking::try::do_call() Line 557 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:557)
[Inline Frame] test-runner.exe!std::panicking::try() Line 520 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:520)
[Inline Frame] test-runner.exe!std::panic::catch_unwind() Line 358 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:358)
test-runner.exe!std::rt::lang_start_internal() Line 174 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:174)
test-runner.exe!std::rt::lang_start<tuple$<>>(void(*)() main, __int64 argc, unsigned char * * argv, unsigned char sigpipe) Line 194 (c:\Users\valja\.rustup\toolchains\1.84.0-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:194)
test-runner.exe!main() (Unknown Source:0)

It seems that self.directives is NULL here:

/// Returns `true` if this program has a `"use strict"` directive.
pub fn has_use_strict_directive(&self) -> bool {
self.directives.iter().any(Directive::is_use_strict)
}

{__0={buf={ptr=NonNull(0x0000000000000008: 0x0000000000000008 {span={start=??? end=??? } expression={span={start=??? end=??? } value={__0=??? } ...} ...}) ...} ...} }

Code to reproduce (click to expand)

use oxc::{
  allocator::{self, Allocator, IntoIn},
  ast::{AstKind, Visit},
  semantic::Semantic,
};

/// # Panics
pub fn main() {
  let alloc = oxc::allocator::Allocator::default();
  let semantic = parse_file("test.ts", &alloc);
  let visitor = TypeVisitor {
    semantic: &semantic,
    assertions: vec![vec![Assertion { expr: "Foo", expected_type: "Foo" }]],
  };
  visitor.run();
}

fn parse_file<'a>(path: &'_ str, alloc: &'a Allocator) -> Semantic<'a> {
  let source_text: allocator::String = "class Foo {}".to_string().into_in(alloc);
  let source_text = source_text.into_bump_str();
  let source_type = oxc::span::SourceType::from_path(path).unwrap();
  let parser = oxc::parser::Parser::new(alloc, source_text, source_type);

  let parse_result = parser.parse();

  let program = parse_result.program;
  let builder = oxc::semantic::SemanticBuilder::new().with_check_syntax_error(true);
  let semantic_result = builder.build(&program);

  semantic_result.semantic
}

pub struct Assertion<'a> {
  pub expr: &'a str,
  pub expected_type: &'a str,
}

pub struct TypeVisitor<'a> {
  pub semantic: &'a Semantic<'a>,
  pub assertions: Vec<Vec<Assertion<'a>>>,
}

impl TypeVisitor<'_> {
  /// # Panics
  pub fn run(&self) {
    // !!! Removing this statement fixes the crash
    let _ = self.assertions.iter().flat_map(|x| x.iter().map(|x| x.expr)).collect::<String>();

    let source_text = self.semantic.source_text();
    let assertions = self.assertions.iter().flat_map(|x| x.iter());
    let mut visitor = TypeVisitorImpl { _source_text: source_text, _assertions: assertions };
    let AstKind::Program(program) =
      self.semantic.nodes().root_node().expect("root node to exist").kind()
    else {
      panic!("Expected root AST node to be Program");
    };
    visitor.visit_program(program);
  }
}

struct TypeVisitorImpl<'a, T: Iterator<Item = &'a Assertion<'a>>> {
  _source_text: &'a str,
  _assertions: T,
}

impl<'a, T: Iterator<Item = &'a Assertion<'a>>> Visit<'a> for TypeVisitorImpl<'a, T> {}

System Details

  • oxc: 0.44.0
  • rustc 1.84.0 (9fc6b4312 2025-01-07)
    binary: rustc
    commit-hash: 9fc6b43126469e3858e2fe86cafb4f0fd5068869
    commit-date: 2025-01-07
    host: x86_64-pc-windows-msvc
    release: 1.84.0
    LLVM version: 19.1.5
    
  • os: Windows 11

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory - Bug

    Type

    No type

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions