Rooting hazards in PaymentRequest.cpp and PaymentRequestUpdateEvent.cpp -- Optional<JSObject*>
Categories
(Core :: DOM: Web Payments, defect)
Tracking
()
People
(Reporter: sfink, Assigned: bzbarsky)
References
Details
(Keywords: csectype-uaf, sec-high, Whiteboard: [post-critsmash-triage])
Attachments
(1 file)
This code:
PaymentDetailsUpdate details;
if (!details.Init(aCx, aValue)) { ... }
nsresult rv = mRequest->IsValidDetailsUpdate(details, true );
is a rooting hazard. details contains an Optional<JSObject*> that could be populated by Init(), then invalidated by a GC in Init() or probably other code, then used in IsValidDetailsUpdate().
I am embarrassed to say that I convinced myself that this was a false positive -- or rather, that this is a false positive:
PaymentDetailsUpdate details;
if (!details.Init(aCx, aValue)) {
mRequest->AbortUpdate(NS_ERROR_TYPE_ERR);
JS_ClearPendingException(aCx);
return;
}
because (1) Optional<JSObject*> isn't populated with anything initially, so that zero-arg ctor really shouldn't be considered to start its live range; and (2) if Init() fails and we run this error handling code, then the analysis will think there's a hazard because the destructor uses 'details' only the destructor won't actually look at any pointers. So I spent a couple of days implementing automatic inference for the first and an annotation for the second, then pushed to try and realized I'm an idiot -- what if Init() doesn't fail? 'details' is populated, and used. I suppose all my work at least makes it report the real hazard and not a false positive.
I don't have the try result back yet, but it had better give me a hazard.
This is autogenerated code. I'm not entirely clear on the correct fix. One option would be to give PaymentDetailsUpdate a trace() method and put 'details' in a Rooted<> here. I don't know if that requires doing the full work required for bug 1517829? Unfortunately this appears to be a real hazard, not a false positive.
Hazard:
Function '_ZN7mozilla3dom25PaymentRequestUpdateEvent16ResolvedCallbackEP9JSContextN2JS6HandleINS4_5ValueEEE$void mozilla::dom::PaymentRequestUpdateEvent::ResolvedCallback(JSContext*, JS::Handle<JS::Value>)' has unrooted 'details' of type 'mozilla::dom::PaymentDetailsUpdate' live across GC call '_ZN7mozilla3dom14PaymentRequest11AbortUpdateE8nsresult$void mozilla::dom::PaymentRequest::AbortUpdate(uint32)' at dom/payments/PaymentRequestUpdateEvent.cpp:66
PaymentRequestUpdateEvent.cpp:64: Call(24,25, details.PaymentDetailsUpdate())
PaymentRequestUpdateEvent.cpp:65: Assign(25,26, __temp_10 := aValue*)
PaymentRequestUpdateEvent.cpp:65: Call(26,27, __temp_9 := details.Init(aCx*,__temp_10*,"Value",0))
PaymentRequestUpdateEvent.cpp:65: Assume(27,28, !__temp_9*, true)
PaymentRequestUpdateEvent.cpp:66: Call(28,29, __temp_11 := this*.mRequest.operator->())
PaymentRequestUpdateEvent.cpp:66: Call(29,30, __temp_11*.AbortUpdate(2152923162)) [[GC call]]
PaymentRequestUpdateEvent.cpp:67: Call(30,31, JS_ClearPendingException(aCx*))
PaymentRequestUpdateEvent.cpp:68: Call(31,52, details.~PaymentDetailsUpdate())
GC Function: _ZN7mozilla3dom14PaymentRequest11AbortUpdateE8nsresult$void mozilla::dom::PaymentRequest::AbortUpdate(uint32)
static already_AddRefed<mozilla::dom::PaymentRequestManager> mozilla::dom::PaymentRequestManager::GetSingleton()
void mozilla::dom::PaymentRequestManager::PaymentRequestManager() [[complete_ctor]]
void mozilla::dom::PaymentRequestManager::PaymentRequestManager()
uint32 mozilla::Preferences::RegisterCallbackAndCall(mozilla::TypedPrefChangeFunc<nsTArray<nsTString<char16_t> > >, int8[38]*, const nsTArray<nsTString<char16_t> >*)[N], T*) [with int N = 38; T = nsTArray<nsTString<char16_t> >; typename mozilla::TypedPrefChangeFunc<T>::Type = mozilla::TypedPrefChangeFunc<nsTArray<nsTString<char16_t> > >]
uint32 mozilla::Preferences::RegisterCallbackAndCall((void)(int8*,void*)*, nsTSubstring<char>*, void*, uint32)
IndirectCall: aCallback
Updated•6 years ago
|
Updated•6 years ago
|
Updated•6 years ago
|
![]() |
Assignee | |
Comment 1•6 years ago
|
||
This is autogenerated code.
No, it's not. It's handwritten, buggy code. Autogenerated code knows about dictionary rooting and roots them. See https://searchfox.org/mozilla-central/search?q=RootedDictionary&case=true&path=__GENERATED__%2Fdom%2Fbindings. And the autogenerated dictionary in this case already has a TraceDictionary method, because the codegen knows it needs tracing.
And I thought our rooting analysis already caught this stuff; why didn't the tree go red when it landed? :(
I'm not entirely clear on the correct fix.
The correct fix is to use
RootedDictonary<PaymentDetailsUpdate> update(aCx);
as documented at https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings#Dictionary_types fourth paragraph.
Bug 1285377 kinda tracks switching to a Rooted-like setup, but that wouldn't have helped here, because the basic bug is that the author of this code just didn't root things that need rooting.
![]() |
Assignee | |
Updated•6 years ago
|
![]() |
Assignee | |
Comment 2•6 years ago
|
||
So I think the good news is that all this code is disabled on non-nightly builds (hardcoded in PaymentRequest::PrefEnabled), and even on nightly it's disabled by default unless you flip the "dom.payments.request.enabled" preference, and even then it's disabled outside en-US, though by the time you get to that point that's not much comfort.
In particular, I don't think we need to backport this to anywhere, if I'm right about the above.
Comment 3•6 years ago
|
||
Yes, Web Payments are disabled by default and development is on indefinite hiatus.
![]() |
Assignee | |
Comment 4•6 years ago
|
||
![]() |
Assignee | |
Comment 5•6 years ago
|
||
![]() |
||
Comment 6•6 years ago
|
||
Updated•6 years ago
|
Updated•6 years ago
|
Updated•6 years ago
|
Description
•