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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions