-
Notifications
You must be signed in to change notification settings - Fork 5k
inspect: clear exceptions when walking Proxy prototype chain #29845
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
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
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
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
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.
🟣 Heads-up (pre-existing, not introduced by this PR): the same
getPrototype(globalObject).getObject()null-deref pattern this PR fixes still exists atsrc/bun.js/bindings/napi.cpp:1837innapi_get_all_property_names. A Proxy with a throwinggetPrototypeOf/getOwnPropertyDescriptortrap will crash there the same way; PR #29642 reportedly extends the fix to napi, so you may want to apply the sameCLEAR_IF_EXCEPTION+ empty-value guard there or land that PR alongside.Extended reasoning...
Summary
This PR correctly fixes a null-pointer crash in
forEachPropertyImpl(bindings.cpp:5364-5369) whereiterating->getPrototype(globalObject).getObject()dereferences a nullJSCellwhen a Proxy'sgetPrototypeOftrap throws (or a prior trap leaves an exception pending). However, the identical vulnerable pattern remains untouched atsrc/bun.js/bindings/napi.cpp:1837insidenapi_get_all_property_names:This is pre-existing — the PR doesn't touch napi.cpp, add callers to it, or change its trigger likelihood. It is flagged here only because it is the exact same bug class being fixed, and the duplicate-PR bot on this PR already notes that #29642 covers the napi side.
Code path
napi_get_all_property_namesis reachable from any native addon. When called withkey_mode == napi_key_include_prototypesand a non-trivialkey_filter(e.g.napi_key_writable/napi_key_enumerable/napi_key_configurable), the function enters the descriptor-filtering branch at napi.cpp:1833. For each key it walks the prototype chain:current_object->getOwnPropertyDescriptor(...). Ifcurrent_objectis aProxyObject, this invokes the JSgetOwnPropertyDescriptortrap, which can throw. On throw,getOwnPropertyDescriptorreturnsfalsewith a pending exception, and the loop body executes.current_object->getPrototype(globalObject). With an exception already pending — or ifcurrent_objectis a Proxy whosegetPrototypeOftrap throws —ProxyObject::getPrototypeshort-circuits viaRETURN_IF_EXCEPTIONand returns an emptyJSValue({ })..getObject()is called on that empty value. On JSVALUE64, an emptyJSValueis encoded as0, which passes theisCell()check ((0 & NotCellMask) == 0), soasCell()returnsnullptrandasCell()->getObject()dereferences null — crashing before theif (!proto)check on line 1838 ever runs.Why existing code doesn't prevent it
There is no
ThrowScope/CatchScopeexception clearing between thegetOwnPropertyDescriptorcall and thegetPrototypecall, and the result ofgetPrototype()is chained directly into.getObject()without an empty-value check. Theif (!proto) break;guard on line 1838 only handles the case wheregetPrototype()legitimately returnsjsNull()/jsUndefined()(for which.getObject()safely returnsnullptr); it cannot help when.getObject()itself segfaults on an empty value.Step-by-step proof
const p = new Proxy({}, { getPrototypeOf() { throw new Error('boom'); } });and passespto a native addon.napi_get_all_property_names(env, p, napi_key_include_prototypes, napi_key_writable, napi_key_numbers_to_strings, &result).Object.prototypesincegetOwnPropertyNamesincludes prototypes here), enters the filter loop, andgetOwnPropertyDescriptoron the proxy returns false (no own descriptor on the empty target).current_object->getPrototype(globalObject)invokes thegetPrototypeOftrap → throws → returns emptyJSValue.emptyJSValue.getObject()→isCell()true →asCell()=nullptr→nullptr->getObject()→ SIGSEGV.The same crash occurs without a
getPrototypeOftrap if thegetOwnPropertyDescriptortrap throws at step 3, because step 4 then short-circuits on the pending exception and still returns empty.Impact
Process crash (null-pointer dereference) in any native addon that calls
napi_get_all_property_nameswithnapi_key_include_prototypes+ a descriptor filter on an object whose prototype chain contains a hostile/buggy Proxy. Lower exposure than theBun.inspectpath fixed in this PR (requires a native addon and a specific key-mode/filter combo), but identical mechanism.Suggested fix
Apply the same pattern this PR uses in bindings.cpp:
Since #29642 reportedly already does this, the simplest path is to land that PR alongside this one rather than expanding scope here.