Skip to content

flinmemory is a false positive for mbar externals in headless builds; dereferencing variabledata segfaults #896

Open

Description

@jsavin

Rationale

flinmemory is not trustworthy per-type in the headless build. For menubar (mbar) externals the headless stub marks the value resident without loading it, so variabledata still holds a raw dbaddress rather than a pointer. Any code that trusts the flag and dereferences the payload segfaults.

This is a landmine for every generic ODB walker, independent of the tool that found it: the flag means "resident" everywhere else, so the natural if (flinmemory) { ... } guard is a false positive exactly where it is most dangerous.

Current State

frontier-cli/stubs/headless_menu_stubs.c:618:

boolean menuverbinmemory_context (const db_context *ctx, hdlexternalvariable hvariable) {
	/*
	 * Menu externals are not materialized during headless migration.
	 * Mark as "in memory" to skip actual loading, but keep the address for
	 * packing.
	 */
	(void) ctx;
	(**hvariable).flinmemory = true;
	return true;
}

ensure_external_in_memory() (Common/source/langexternal.c:916) dispatches idmenuprocessor here, so a caller that materializes an external and then reads variabledata gets a dbaddress reinterpreted as a handle.

Verified reproduction (2026-08-15, pure-C walker, no interpreter):

  • Walking mainResponder.root and dereferencing variabledata for mainResponder.menu -> SIGSEGV, exit 139, reproducible.
  • Materialization itself "succeeds" (returns true); the crash is on first dereference afterwards.
  • Same failure shape on any mbar: 43 in Virgin.root, 1 in mainResponder.root, 11 in manila.root.

Independent corroboration: mainResponder.menu is also the single mbar the 2026-08-11 ODB census failed to measure -- it records size -1 (its catchable-failure sentinel) for exactly that value. Two tools, two different mechanisms, same value.

Related exposure

ut_export_walk_table() (frontier-cli/main.c:2444) guards subtable recursion with if ((**hv).flinmemory). For idtableprocessor that guard is merely incomplete (a non-resident subtable is silently skipped, so an exporter can omit an entire subtree). The concern here is the general one: the flag is being used as ground truth for residency, and for at least one external kind it is not.

Also worth noting for anyone auditing this area: a second stub, menuverbpack-adjacent code, exists specifically to "keep the address for packing", so the current behavior is deliberate for the migration path -- it is the generic readers that are unsafe, not the migration.

Desired Outcome

  1. Make the contract explicit: either have menuverbinmemory_context fail honestly in headless (returning false so callers can report), or introduce a distinct predicate (e.g. external_payload_is_resident()) that generic walkers can trust per-type.
  2. Audit existing flinmemory readers for the same assumption.
  3. Document the trap wherever residency flags are described, so the next walker author does not rediscover it with a segfault.
  4. Regression test: materialize an mbar in a headless build and assert the payload is either genuinely loaded or reported as unavailable -- never a dereferenceable-looking lie.

Workaround in place

--diff-roots (root-build Step 1) compares mbar and wptext at the stored level (raw disk bytes at the external's address) and never materializes them. wptext is excluded for a different reason -- force-materializing those exhausts memory on a full-root walk (Common/source/db_format.c:1694) -- and the two rationales are kept distinct in that tool's output so they are not conflated later.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority/p1High priority - address soonscope/medium1-2 weeks, requires design worktype/bugDefect in existing functionality, needs fixworkstream/runtime-coreRuntime architecture, memory, lifecycle, and global context

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions