Repository navigation
fix(runtime): reject the handle band before reading a symbol receiver's GcHeader #11327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
c97ff9d
test(fetch): cover symbol-keyed own properties on Request/Response/He…
TheHypnoo 847accd
fix(runtime): reject the handle band before reading a symbol receiver…
TheHypnoo 77941ce
test(parity): ratchet the fetch node-suite floor to 13/13
TheHypnoo 17b1d84
docs: key the changelog fragment to PR 11327
TheHypnoo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
18 changes: 18 additions & 0 deletions
18
changelog.d/11327-fetch-symbol-property-handle-band-segv.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| Fixed `Reflect.set`/`obj[sym] = v` segfaulting on a native Web Fetch handle | ||
| (`Request`/`Response`/`Headers`) — e.g. `Reflect.set(new Request(url), Symbol.for("x"), v)`, | ||
| the pattern Astro's `@astrojs/node` adapter uses to tag every request with its | ||
| client address. `set_symbol_property`'s frozen/non-extensible guard (and its | ||
| `symbol_property_is_non_writable` mirror) gated the `GcHeader` dereference on a | ||
| hand-typed `obj_key >= 0x10000` floor — an order of magnitude below | ||
| `HANDLE_BAND_MAX` (0x100000) — paired with `is_valid_obj_ptr`, whose doc | ||
| explicitly warns it does *not* reject the handle band by itself. A Web Fetch | ||
| handle id (`[0x40000, 0xE0000)`) sailed through both checks and | ||
| `(obj_key - GC_HEADER_SIZE) as *const GcHeader` read unmapped memory. Both call | ||
| sites now use `try_read_gc_header`, which classifies the whole handle band | ||
| (and alignment) before any dereference and returns `None` for handle | ||
| receivers — the same guard already used elsewhere in the runtime for this | ||
| exact class of bug (#1843/#4004/#4800). | ||
|
|
||
| Test: `test-parity/node-suite/fetch/symbol-property-set.ts` (Reflect.set/get, | ||
| direct `obj[sym]`, overwrite, `Object.getOwnPropertySymbols`, delete, and an | ||
| untyped-parameter write, on `Request`/`Response`/`Headers`). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Symbol-keyed own properties on native fetch objects (Request / Response / | ||
| // Headers). Frameworks tag these objects with private symbols — e.g. Astro's | ||
| // node adapter does `Reflect.set(request, clientAddressSymbol, ip)` on every | ||
| // request — so the write must behave like on any ordinary object. | ||
|
|
||
| const registered = Symbol.for("parity.fetch.registered"); | ||
| const local = Symbol("parity.fetch.local"); | ||
|
|
||
| function exercise(label: string, obj: any) { | ||
| console.log(label, "Reflect.set registered:", Reflect.set(obj, registered, "r1")); | ||
| console.log(label, "Reflect.get registered:", Reflect.get(obj, registered)); | ||
|
|
||
| obj[local] = "l1"; | ||
| console.log(label, "direct get local:", obj[local]); | ||
|
|
||
| obj[registered] = "r2"; | ||
| console.log(label, "overwrite registered:", obj[registered]); | ||
|
|
||
| const syms = Object.getOwnPropertySymbols(obj); | ||
| console.log(label, "own symbols include registered:", syms.includes(registered)); | ||
| console.log(label, "own symbols include local:", syms.includes(local)); | ||
|
|
||
| console.log(label, "delete local:", delete obj[local], obj[local]); | ||
| } | ||
|
|
||
| function viaUntypedParam(label: string, target: any) { | ||
| // Reach the write through an untyped parameter, the shape framework code uses. | ||
| console.log(label, "param Reflect.set:", Reflect.set(target, local, "p1")); | ||
| console.log(label, "param get:", target[local]); | ||
| } | ||
|
|
||
| const req = new Request("http://localhost/path", { method: "POST" }); | ||
| const res = new Response("body", { status: 201 }); | ||
| const headers = new Headers({ "x-a": "1" }); | ||
|
|
||
| exercise("request", req); | ||
| exercise("response", res); | ||
| exercise("headers", headers); | ||
|
|
||
| viaUntypedParam("request", new Request("http://localhost/")); | ||
| viaUntypedParam("response", new Response(null)); | ||
| viaUntypedParam("headers", new Headers()); | ||
|
|
||
| // The native state must survive the symbol writes. | ||
| console.log("request still works:", req.method, req.url); | ||
| console.log("response still works:", res.status); | ||
| console.log("headers still works:", headers.get("x-a")); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,8 +68,8 @@ | |
| "total": 69 | ||
| }, | ||
| "fetch": { | ||
| "pass": 12, | ||
| "total": 12 | ||
| "pass": 13, | ||
| "total": 13 | ||
| }, | ||
| "fs": { | ||
| "pass": 168, | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Check symbol attributes even when the receiver has no GC header.
When a Fetch handle has an existing symbol property with
writable: false,try_read_gc_headerreturnsNone. The setter then skips this attribute check and can overwrite the property throughjs_object_set_symbol_property. Keep the frozen-flag check inside the GC-header branch, but move the per-symbol writable check outside that branch. The attribute table accepts handle keys, and the exported setter calls this function directly. (raw.githubusercontent.com)🤖 Prompt for AI Agents