Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions changelog.d/7804-addr-class-scans-ext-crates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
**The addr-class audit now scans `crates/perry-ext-*`** (#7272), bringing 18 previously-invisible sites into the gate.

`scripts/addr_class_inventory.py` stopped at `perry-runtime/src` and `perry-stdlib/src`, while its sibling `gc_store_site_inventory.py` has globbed `crates/perry-ext-*/src` all along — so the two audits disagreed about what counts as first-party runtime code.

That was not academic. #6826 moved the HTTP server out of `crates/perry-stdlib/src/http.rs` into `crates/perry-ext-http/`, and this gate then reported its 11 handle-floor sites as `baseline says 11, found 0 — lower it to 0`. The sites had not been fixed; they had walked out of the gate's field of view, and the ratchet's own bookkeeping invited someone to ratify that as progress. **A gate whose coverage shrinks silently when code moves** is the failure mode CLAUDE.md's "four ways a gate can be unable to fail" is about, with the twist that here the shrinkage announces itself as a win.

Scanned files go from 826 to **1027**. The 18 newly-visible sites are recorded, not rewritten:

* **10 handle-floor**, baselined per file — `perry-ext-events` (3), `perry-ext-http/agent.rs` (3), `perry-ext-http/lib.rs` (2), `perry-ext-exponential-backoff` (1), `perry-ext-fastify` (1). Five of those are the HTTP server's, i.e. the ones the stale entry was about.
* **8 band-literal**, allowlisted with justifications — each is a wrapper asking "is this f64 payload a small registry handle rather than a heap pointer?" before dereferencing, which is exactly what `addr_class::is_handle_band` answers. They are re-typed rather than called because the ext crates reach perry-runtime through its public surface and `is_handle_band` is not on it. Exporting it is the real fix and belongs with #7448's `ptr_is_tracked_heap_object` export rather than buried in a scan-scope change.

**Verified the widened gate can fail**, rather than assuming it: planting `if (obj as usize) < 0x100000 {` in `perry-ext-exponential-backoff` makes the audit exit 1 and name the file and line; removing it returns exit 0. Worth recording that a first attempt at that sabotage used `p < 0x10000` and did *not* trip the rule — the detector keys on specific shapes, so "I added a magic number and nothing happened" is not evidence the scan is dark.

The sibling `gc_store_site_inventory.py` and `check_file_size.sh` both stay green.
13 changes: 13 additions & 0 deletions scripts/addr_class_allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,16 @@ crates/perry-runtime/src/closure/dispatch/ | * | pre-existing GcHeader probe pre
crates/perry-runtime/src/bun_compat/string_width.rs | 0xE0000..=0xE007F | Unicode "Tags" codepoint block (U+E0000..U+E007F) tested against a char, not a handle-band address
crates/perry-runtime/src/bun_compat/width_tables.rs | * | pure Unicode East-Asian-width codepoint-range table; every hex literal is a Unicode code point (e.g. U+F0000 / U+100000 SPUA-A/B planes), never a handle-band address
crates/perry-runtime/src/array/collection_tag_tests.rs | * | unit tests for the #7765 receiver-tag gate: they read AND re-stamp `GcHeader.obj_type` on an address they allocated themselves, which is the whole subject under test. `try_read_gc_header` cannot serve them (it hands out a shared reference, and the recycling test must WRITE the tag to model `arena_alloc_gc` handing the bytes to the next owner).

# #7272: band literals in `crates/perry-ext-*`, which entered this gate's scope
# with the widened scan roots. Each is a wrapper deciding "is this f64 payload a
# small registry handle rather than a heap pointer?" before dereferencing --
# the same question `addr_class::is_handle_band` answers, re-typed. They are
# recorded rather than rewritten because the ext crates reach perry-runtime
# through its public surface and `is_handle_band` is not on it; exporting it is
# the actual fix and belongs with #7448's `ptr_is_tracked_heap_object` export,
# not buried in a scan-scope change.
crates/perry-ext-events/src/lib.rs | const EVENT_EMITTER_HANDLE_ID_END | #7272: the crate's own handle-id range end, a registry bound rather than a heap-address band test
crates/perry-ext-net/src/jsvalue.rs | * | #7272: socket/server handle-vs-pointer discrimination before dereference; re-types the handle band instead of calling addr_class::is_handle_band, which perry-runtime does not export
crates/perry-ext-ratelimit/src/lib.rs | (obj as usize) >= 0x100000 | #7272: same handle-vs-pointer guard before an ObjectHeader read
crates/perry-ext-slugify/src/lib.rs | (obj as usize) < 0x100000 | #7272: same handle-vs-pointer guard before an ObjectHeader read
25 changes: 23 additions & 2 deletions scripts/addr_class_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@
"crates/perry-stdlib/src",
)

# #7272: `crates/perry-ext-*/src` is first-party runtime code and is scanned
# too, matching the sibling gate `gc_store_site_inventory.py`, whose
# `iter_scan_roots()` has globbed these all along.
#
# The two audits disagreeing was not academic. #6826 moved the HTTP server out
# of `perry-stdlib/src/http.rs` into `crates/perry-ext-http/`, and this gate
# reported the file's 11 handle-floor sites as "found 0 — lower it to 0". They
# had not been fixed; they had walked out of the gate's field of view, and the
# ratchet's own bookkeeping invited someone to ratify that as progress. A gate
# whose coverage shrinks silently when code moves is the shape CLAUDE.md's
# "four ways a gate can be unable to fail" is about.
EXT_CRATE_GLOB = "perry-ext-*"

# The module that owns the band constants/predicates, and the collector
# internals that legitimately manipulate GcHeader layout directly.
EXCLUDED_PREFIXES = (
Expand Down Expand Up @@ -215,8 +228,16 @@ def scan_text(rel_path: str, text: str) -> list[Finding]:
def collect_inventory() -> tuple[list[Finding], int]:
findings: list[Finding] = []
files_scanned = 0
for root in SCAN_ROOTS:
for path in sorted((REPO_ROOT / root).rglob("*.rs")):
scan_dirs = [REPO_ROOT / root for root in SCAN_ROOTS]
scan_dirs.extend(
src
for src in (
ext / "src" for ext in sorted((REPO_ROOT / "crates").glob(EXT_CRATE_GLOB))
)
if src.is_dir()
)
for root in scan_dirs:
for path in sorted(root.rglob("*.rs")):
rel_path = path.relative_to(REPO_ROOT).as_posix()
# Skip parked/hidden trees (e.g. `.value.parked/`) — not compiled.
if any(part.startswith(".") for part in rel_path.split("/")):
Expand Down
16 changes: 16 additions & 0 deletions scripts/addr_class_ratchet_baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,19 @@ lone-valid-obj-ptr | crates/perry-runtime/src/util_mime.rs | 1
lone-valid-obj-ptr | crates/perry-runtime/src/util_style_text.rs | 1
lone-valid-obj-ptr | crates/perry-runtime/src/value/dyn_index.rs | 2
lone-valid-obj-ptr | crates/perry-runtime/src/value/to_string.rs | 1

# #7272: `crates/perry-ext-*` entered this gate's scope. These ten sites are
# not new code — they were invisible because the scan roots stopped at
# perry-runtime/perry-stdlib while the sibling gate
# (gc_store_site_inventory.py) had globbed the ext crates all along.
#
# Five of them are the HTTP server's, which #6826 moved out of
# crates/perry-stdlib/src/http.rs. This file used to carry an entry for that
# path, and once the file vanished the gate reported "baseline says 11, found
# 0 -- lower it to 0": an invitation to ratify a coverage loss as a fix. They
# are baselined here at their real counts instead.
handle-floor | crates/perry-ext-events/src/lib.rs | 3
handle-floor | crates/perry-ext-exponential-backoff/src/lib.rs | 1
handle-floor | crates/perry-ext-fastify/src/server.rs | 1
handle-floor | crates/perry-ext-http/src/agent.rs | 3
handle-floor | crates/perry-ext-http/src/lib.rs | 2
Loading