Skip to content

dastest --deser: nondeterministic AV in TypeDecl::getMangledName under JIT'd RTTI over deserialized modules (already_exists gc lifetime) #3439

Description

@borisbat

Seen once on extended_checks (windows, 64) "Test ser/deser" for PR #3436 (tip 5aff6a3); a plain re-run of the same job on the same commit passed, linux extended_checks and local Windows runs of the same lane are green — nondeterministic, heap-layout dependent.

Crash

During the --deser pass, right after tests/jit_tests/assert.das (new in #3433 — first time the ser/deser lane serializes the jit_tests programs):

CRASH: EXCEPTION_ACCESS_VIOLATION (0xC0000005), reading address 0x0
  das::TypeDecl::getMangledName
  das::DebugInfoHelper::makeTypeInfo
  das::DebugInfoHelper::makeVariableDebugInfo
  das::DebugInfoHelper::makeStructureDebugInfo
  das::rtti_builtin_module_for_each_structure
  jit_register_Module_Rtti (JIT'd test code)
  ... pinvoke / runWithCatch / dastest deserialize_path (dastest.das:436/444)

A JIT'd test walks structures of a deserialized module via RTTI and getMangledName dereferences a null TypeDecl link.

Suspected mechanism

The dastest rail (rtti_ast_serializer_deserialize_program) deserializes under a gc_guard, whose destructor sweeps unclaimed nodes — and the guard scope ends before the test callback (simulate + run) executes. Survival of the deserialized AST relies on finalizeModule's collect moving nodes onto module roots, but the already_exists path deliberately skips that:

// module_builtin_ast_serialize.cpp, finalizeModule
} else {
    // we DO NOT collect something which is "already exists"
    // we leave it hanging, and we keep links to types from other modules and let them claim
    // this_mod->gc_collect(gc_root::gc_get_active_root());
}

Any node reachable from a surviving module but not claimed by a later module's collect gets swept with the guard while still referenced — a dangling AST link that reads as intact memory (reuse cache) until allocation timing says otherwise. Same corruption class as the env-rail dangling-active-root bug fixed in #3436, one rail over.

A DAS_GC_DEBUG=1 run of the dastest --ser/--deser lane (not just tests-cpp) with the jit_tests included should turn the nondeterministic AV into a deterministic poisoned-read and pinpoint the unclaimed node.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions