-
Notifications
You must be signed in to change notification settings - Fork 5k
inspect: handle throwing Proxy getPrototypeOf in forEachProperty #29814
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.
🟣 Pre-existing / non-blocking: the identical unguarded
getPrototype(globalObject).getObject()pattern this PR fixes also exists atsrc/jsc/bindings/napi.cpp:1837insidenapi_get_all_property_names— a Proxy with a throwinggetPrototypeOf/getOwnPropertyDescriptortrap in the prototype chain triggers the same null-member-call there. Sibling PR #29642 bundled that napi.cpp fix; flagging so it isn't lost if #29642 is closed in favor of this minimal PR.Extended reasoning...
What
This PR correctly hardens
JSC__JSValue__forEachPropertyImplby capturing the result ofiterating->getPrototype(globalObject), clearing exceptions, and breaking on an emptyJSValuebefore calling.getObject(). The exact same vulnerable pattern remains atsrc/jsc/bindings/napi.cpp:1837innapi_get_all_property_names:There is no
CLEAR_IF_EXCEPTION/RETURN_IF_EXCEPTIONbetween thegetOwnPropertyDescriptorcall (which can throw via a ProxygetOwnPropertyDescriptortrap) and thegetPrototype()call (which can throw via a ProxygetPrototypeOftrap), and no empty-JSValueguard before.getObject(). Theif (!proto)check on the next line is too late — the null member call has already happened inside.getObject().Why existing code doesn't prevent it
The only exception check in this region is
NAPI_RETURN_IF_EXCEPTION(env)at line 1823, which runs before the filtering loop. Inside the loop, whencurrent_objectis a Proxy:getOwnPropertyDescriptorinvokes the Proxy'sgetOwnPropertyDescriptortrap; if it throws, the call returnsfalsewith a pending exception, so thewhilebody executes.getPrototypethen either short-circuits on the pending exception or invokes the Proxy'sgetPrototypeOftrap (which can also throw), and in either case returns an emptyJSValue.JSValueis encoded as0, soisCell()is true (0 & NotCellMask == 0) andasCell()isnullptr.JSValue::getObject()therefore callsasCell()->getObject()on a null cell — the same UBSAN null-pointer-member-call this PR fixes inbindings.cpp.Step-by-step proof
napi_get_all_property_names(env, obj, napi_key_include_prototypes, napi_key_enumerable, napi_key_numbers_to_strings, &result)whereobj's prototype chain containsnew Proxy({}, { getPrototypeOf() { throw 0 } }).key_filter & filter_by_any_descriptoris non-zero (line 1827) → enters the per-key filtering loop.key_mode == napi_key_include_prototypes→ enters the prototype-climbwhileat line 1836.current_object.getOwnPropertyDescriptorreturnsfalse(key not on the Proxy target) → loop body runs.current_object->getPrototype(globalObject)invokes the ProxygetPrototypeOftrap, which throws → returns emptyJSValue..getObject()on the empty value:isCell()→ true,asCell()→nullptr,nullptr->getObject()readsm_type→ null-pointer member call (same fingerprintf20677b52d4735c2crash class).The same outcome occurs if the Proxy instead defines a throwing
getOwnPropertyDescriptortrap: the trap throws,getOwnPropertyDescriptorreturnsfalsewith a pending exception, andgetPrototype()returns empty because of the pending exception.Impact & fix
Impact is narrower than the
Bun.inspectcase (requires a native addon callingnapi_get_all_property_nameswithnapi_key_include_prototypesplus an enumerable/writable/configurable filter on user-controlled objects), but it is the same crash class. The fix is identical to what this PR applies inbindings.cpp: capturegetPrototype()into a localJSValue,RETURN_/CLEAR_IF_EXCEPTION, and bail if it's empty before calling.getObject()(and ideally also check for an exception aftergetOwnPropertyDescriptor).Scope
This is pre-existing — this PR does not touch
napi.cpp, add callers to it, or change its trigger surface. The author explicitly noted in the timeline that #29642 bundled both theforEachPropertyImplfix and thenapi.cppfix, and that this PR is the "minimal, targeted version" with "Happy to close whichever is redundant once one lands." Flagging only so thenapi.cpphalf isn't lost if #29642 is closed in favor of this PR. Should not block merge.