ReviewOS

zig-utils/zig-gc

TSan: publish owned cell headers before classification

#29
Closed chris opened this 9 days ago ·
9 days ago

Downstream: zig-utils/zig-js#128 Related: #4, #26

Problem

The optional owned-slab classifiers can expose a newly reserved slot before zig-gc initializes and publishes its header. In zig-js's parallel mid-script collector, a conservative stack word can then classify that issued slot and read Header.magic, size, and marked while the allocating mutator is still zeroing/initializing the same memory.

Fresh main TSan evidence reports all three manifestations in one exact mid-GC run:

  • headerForInteriorAddress reading header magic;
  • headerForInteriorAddress reading payload size;
  • claimMark touching the mark bit;

while the peer stack is Allocator.allocBytesAligned -> Heap.allocateCellSlab -> Heap.create.

Design

Add optional binding publication callbacks around owned cell headers:

  • after zig-gc initializes every header field, publish the allocation to the binding before it becomes classifiable;
  • before clearing magic/returning a dead slot, unpublish it so classifiers stop returning the address;
  • bindings without the hooks retain current behavior;
  • hook synchronization supplies the happens-before edge from header initialization to classification.

Acceptance

  • Generic tests prove an owned allocation is unclassifiable before header publication, classifiable afterward, and unclassifiable before reclamation.
  • Preserve create/createBatch ordering, born-grey safety, nursery accounting, OOM behavior, and bindings without hooks.
  • Pass full zig-gc normal and TSan suites.
  • Downstream zig-js adds exact per-slot publication metadata and passes focused normal/TSan mid-GC tests.
  • Fresh full-main tsan-threadfuzz-midgc is suppression-free.
9 days ago

Implemented the publication-order half in 466718d on main: optional binding publication hooks now run after complete header initialization and before owned classification, and unpublication runs before finalization/header clearing. Validation: zig test src/heap.zig (36/36) and zig build test -Dtsan=true passed. Keeping this open until the downstream zig-js classifier change and exact TSan mid-GC reproduction are green.

9 days ago

Follow-up 092d8d7 adds the optional batch-publication hook and verifies it in the heap tests, so createBatch initializes every header and then publishes the full batch once. All 36 heap tests pass.

9 days ago

Completed by 466718d and 092d8d7, with downstream zig-js 58c70821 / 17624edf. All 36 heap tests, focused downstream normal/TSan tests, 16/16 parallel-GC tests, and the exact CI tsan-threadfuzz-midgc job in run 29465812082 passed suppression-free. The original header magic/size/mark construction races are gone.

Sign in to comment on this issue.