Skip to content

fix(gc_effects): exempt aHash's random-source init from the indirect-call taint - #12119

Draft
steinybot wants to merge 18 commits into
PerryTS:mainfrom
steinybot:steiny/ci2-gc-effects
Draft

steinybot wants to merge 18 commits into
PerryTS:mainfrom
steinybot:steiny/ci2-gc-effects

Conversation

@steinybot

@steinybot steinybot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Merge in this order:

  1. fix(docs): the my-bindings fixture reads buffers inside a no-GC scope #12112
  2. fix(ext): ext-crate tests root values through RuntimeHandleScope #12113
  3. fix(runtime): audit the new raw stores and classify addresses with addr_class #12114
  4. ci: register the dark GC witnesses and isolate the new test counters #12115
  5. refactor: split five files back under the 2000-line cap #12116
  6. fix(runtime): root raw handles across calls and correct the handle ratchets #12117
  7. fix(runtime): instanceof never mints a class object for a builtin id #12118
  8. fix(gc_effects): exempt aHash's random-source init from the indirect-call taint #12119 (this PR)
  9. docs: regenerate the API reference #12120
  10. fix(runtime): clear the gates that #12109 left red #12121
  11. fix(runtime): match node on the gap-suite regressions #12149

Problem

CI on main is red again after #12025. Commits since then broke check, warnings, cargo-test, gc-call-effects, self-test-checkers, gap-suite and fourteen lint steps, so no PR can show a green build. Each later failure in a job was hidden behind an earlier one.

Solution

Fix each breakage in its own PR. Each PR targets main and holds every fix above it in the order list, so the last PR is the one that can run fully green. Merge in order: each diff then shrinks to its own fix.

In this PR

Fixes gc-call-effects. Since #11992 a thread-local initializer builds an ahash::RandomState, and every HotKey slow path reaches it. The classifier treated aHash's one indirect call as unknown, so a fresh table moved about 740 macOS symbols to Reenters, js_arena_alloc among them. Codegen would then spill roots around those calls.

Changes

  1. Exempt aHash's random-source init (diff). On macOS the call drops a losing DefaultRandomSource, which has no drop glue. On Linux the taint reaches the fixed-seed OnceBox init instead, which only runs the getrandom path; a second rule covers it. A forbid rule on set_random_source keeps both sound. The thread-creation rule also matches Arc's new alloc::rcs::arc path.

    indirect regex ^<?std::sys::thread::unix::Thread>::new|^std::thread::spawnhook::|^<std::thread::spawnhook::|^<alloc::(sync|rcs::arc)::Arc<std::thread::|^core::ptr::drop_glue::<alloc::boxed::Box<std::thread::|^std::thread::lifecycle:: -- thread creation: the closure runs on the NEW thread, whose GC heap and stack are its own; spawn hooks are never registered (see the forbid rule below)

  2. Regenerate the macOS table (diff). 44 rows change, from runtime code that changed since the last regeneration.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7

steinybot and others added 2 commits October 7, 2026 10:39
read_buffer_bytes takes a NoGc scope since PerryTS#12103, so the fixture no
longer compiled, and the warnings and check jobs stopped there. The
fixture's tests also call runtime-backed perry-ffi functions, so they
take perry-ffi with runtime-link as a dev-dependency, like the ext
crates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
The runtime does not export the shadow-stack entry points on native
stack-map targets. The http, net, streams and ws test modules called
them, so they no longer compiled and the warnings job failed.

On those targets the guards' shadow frames rooted nothing. The net and
http tests now hold the values that must survive a collection in a
RuntimeHandleScope. The guards call gc_init(), which registers the
runtime-handle scanner that program startup would register.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
@coderabbitai

coderabbitai Bot commented Oct 6, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

steinybot and others added 15 commits October 7, 2026 11:42
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
…dr_class

The GC store-site, address-classification and SSO inventories failed
on main.

- Store sites: the new raw writes get audit markers. None of them
  stores a GC reference in a heap slot, so no barrier was missing.
- Address classes: the new GcHeader casts use the gc and addr_class
  helpers, and the clone writer's handle floors use is_handle_band.
- SSO: the stream emitter's shape fast paths read a key's header
  through heap_string_header, which declines a short string.

The clone writer now returns an empty string for a STRING_TAG payload
in the handle band, where it read memory before. The method-site
helpers now decline a misaligned or out-of-range address before they
read a header. Both baselines go down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Four lint steps failed on main.

- Local binding audit: receiver_kind reads a typed-array hint. The
  emitted guard checks the receiver's kind at run time, so a wrong
  hint only loses the fast path. The allowlist records it as
  runtime-validated.
- Test registration: the checker now accepts a #[path] declaration
  in a sibling file. native_payload_lifecycle.rs was declared that way
  and its tests already ran.
- Test registration: three test_gap_gc fixtures join the GC corpus.
  The fixture-env parser accepts PERRY_GC_PROTECT_OLD_SWEEP, which
  two of them set. protected_poll_reclaim gets the parity-env line
  that its header describes, so its collections now run.
- Per-test global sinks: the new test counters use per_test_global!.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Each split moves whole functions or inline test modules into a child
file. Module paths and test names do not change.

- dynamic_dispatch.rs: build_direct_method_args moves to
  dynamic_dispatch_args.rs.
- node_stream_readwrite.rs: the chunk and byte collectors move to
  node_stream_chunk_collect.rs.
- method_site.rs: prime_inherited and the constfn tests move to child
  files.
- delete_rest.rs and read_holder.rs: their inline test modules move
  to child files.

The buffer-layout and address-class baselines follow the moved
node_stream lines. Their totals do not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
…tchets

Six ratchet steps failed on main.

- Raw-handle debt: each flagged site now uses with_*_ptr for a call
  that cannot collect, or RuntimeHandle::across_* where an allocating
  call comes before a re-read. The baseline falls from 861 to 848.
- iterator_push_pending read a raw promise and iterator after
  js_array_alloc and js_array_push_f64. It now roots both values and
  re-reads them after each call.
- Native-handle ledger: the zlib Statics rule required a field that
  no longer exists, so it counted 0 tables. It now counts the streams
  and listeners maps, which are keyed by i64 handles.
- Unrooted locals: the collection-point matcher matched "gc(" inside
  no_gc(. Names now match only at the start of an identifier. The
  self-test plants both cases.
- Thread-exit globals and registry lifetime: delete the entries for
  INLINE_OWNING_U32_CACHE and VERDICT_CLASSES, which no longer exist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
The perry-runtime test binary aborted in the stream instanceof test, so
cargo-test stopped before most suites ran.

- instanceof: live_chain_answer and the native-subclass static arm
  passed builtin class ids, such as PassThrough's, to
  class_constructor_ref_value. Only a compiled class has a class
  function object. Both now check is_compiled_class_id and fall back
  to the declared chain walk. A release build no longer mints a bogus
  pinned closure for a builtin id.
- Regex program cells: init_program_cell wrote None through a
  temporary after it zeroed the payload, which copied stack bytes into
  the cell. It writes a field only if the field does not already read
  as None.
- The residual-prototype relocation test registers the object-model
  scanners. Its setPrototypeOf calls fill the canonical-keys trie, and
  without a scanner a copying minor left stale key cells. CI crashed
  on this intermittently.
- Stale tests: the thread-exit closure and symbol side-table tests
  follow PerryTS#11931, and the macOS length test follows PerryTS#10694.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
The guarded writes in init_program_cell sit outside the window of the
existing POINTER_FREE marker, so the store-site inventory flagged
them. They fill a fresh, unpublished cell whose fields hold no GC
pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
…call taint

Since PerryTS#11992, NATIVE_CALLABLE_EXPORTS builds an ahash::RandomState in
its thread-local initializer, and every HotKey slow path reaches that
initializer. aHash's RAND_SOURCE OnceBox init makes one indirect call.
The call drops the losing Box<dyn RandomSource>, which is always a
DefaultRandomSource with no drop glue. The classifier treated the call
as unknown, so about 740 macOS symbols became Reenters, js_arena_alloc
among them.

A new indirect rule exempts that one instantiation. A forbid rule on
set_random_source keeps the exemption sound. The thread-creation rule
also matches Arc's new alloc::rcs::arc path, which the nightly bump
renamed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
The table is regenerated from this branch's release archives with
scripts/gc_call_effects/regen.sh macos-aarch64. With the aHash rule,
js_arena_alloc stays AllocOnly and 809 symbols stay Leaf. The changed
rows come from runtime code that changed after the last regeneration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7
On linux-x86_64 the NATIVE_CALLABLE_EXPORTS taint reaches a different
aHash initializer: OnceBox<[[u64; 4]; 2]>::init for the fixed seeds.
get_fixed_seeds fills that table from getrandom, and the register call
in init is that getrandom or dealloc path. CI's fresh Linux table
still had AllocOnly at 3 and js_arena_alloc as Reenters without this
rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5MSmMLJm6sMdSS5r64ds7

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant