Closed
Bug 1480640
Opened 6 years ago
Closed 6 years ago
False positives from ~RefPtr that won't go to zero
Categories
(Core :: JavaScript: GC, defect)
Core
JavaScript: GC
Tracking
()
RESOLVED
FIXED
mozilla63
People
(Reporter: sfink, Assigned: sfink)
References
(Blocks 1 open bug)
Details
Attachments
(1 file)
(deleted),
patch
|
baku
:
review+
lizzard
:
approval-mozilla-beta+
RyanVM
:
approval-mozilla-esr60+
|
Details | Diff | Splinter Review |
Thanks to fixing up some problems with destructor handling, I got the hazard pasted below.
The problem is the following code:
RefPtr<JS::WasmModule> module = file.mWasmModule;
JS::Rooted<JSObject*> wrappedModule(aCx, module->createObject(aCx));
if (NS_WARN_IF(!wrappedModule)) {
return nullptr;
}
result.set(wrappedModule);
return result;
where 'result' is a Rooted<JSObject*>.
The hazard analysis sees that you're returning a JSObject* extracted from a Rooted<JSObject*>, but after you put it on the stack, ~RefPtr fires, possibly deleting the WasmModule and GCing in the process. (I don't know if that GC is actually possible, but it sure seems to run a scary amount of code.)
But in this case, I don't think the refcount can go to zero, though there's no simple way to prove it. file.mWasmModule has a reference to it, and 'file' seems very likely to survive past the end of this function call.
I'm not sure what I'm going to do with this hazard. Perhaps I should figure out how to prove that ~RefPtr<WasmModule> won't GC?
My other idea was using a RefPtrOfLongLivingThing<T>, though the easiest way to do that would be a simple T*, and I'm not sure how keen people would be about that.
The hazard:
Function '_ZN7mozilla3dom12_GLOBAL__N_134CopyingStructuredCloneReadCallbackEP9JSContextP23JSStructuredCloneReaderjjPv$IDBObjectStore.cpp:JSObject* mozilla::dom::{anonymous}::CopyingStructuredCloneReadCallback(JSContext*, JSStructuredCloneReader*, uint32, uint32, void*)' has unrooted '<returnvalue>' of type 'JSObject*' live across GC call '_ZN6RefPtrIN2JS10WasmModuleEED1Ev$RefPtr<T>::~RefPtr() [with T = JS::WasmModule]' at dom/indexedDB/IDBObjectStore.cpp:1328
IDBObjectStore.cpp:1328: Assign(139,140, return := __temp_46**)
IDBObjectStore.cpp:1328: Call(140,141, wrappedModule.~Rooted())
IDBObjectStore.cpp:1328: Call(141,142, module.~WasmModule>()) [[GC call]]
IDBObjectStore.cpp:1328: Call(142,144, result.~Rooted())
IDBObjectStore.cpp:1333: [[end of function]]
GC Function: _ZN6RefPtrIN2JS10WasmModuleEED4Ev$RefPtr<T>::~RefPtr() [with T = JS::WasmModule]
static void RefPtr<T>::ConstRemovingRefPtrTraits<U>::Release(U*) [with U = JS::WasmModule; T = JS::WasmModule]
static void mozilla::RefPtrTraits<U>::Release(U*) [with U = JS::WasmModule]
void js::AtomicRefCounted<T>::Release() const [with T = JS::WasmModule]
Utility.h:void js_delete(JS::WasmModule*) [with T = JS::WasmModule]
JS::WasmModule.__comp_dtor
_ZN2js4wasm6ModuleD1Ev
_ZN2js4wasm6ModuleD2Ev
void js::wasm::Module::~Module(int32)
_ZN2js21ExclusiveWaitableDataINS_4wasm7TieringEED1Ev
_ZN2js21ExclusiveWaitableDataINS_4wasm7TieringEED2Ev
js::ExclusiveWaitableData<js::wasm::Tiering>::~ExclusiveWaitableData()
_ZN2js13ExclusiveDataINS_4wasm7TieringEED2Ev
js::ExclusiveData<js::wasm::Tiering>::~ExclusiveData()
_ZN2js4wasm7TieringD1Ev
_ZN2js4wasm7TieringD2Ev
void js::wasm::Tiering::~Tiering(int32)
mozilla::Vector<T, N, AllocPolicy>::~Vector() [with T = RefPtr<JS::WasmModuleListener>; long unsigned int MinInlineCapacity = 0ul; AllocPolicy = js::SystemAllocPolicy]
_ZN7mozilla6VectorI6RefPtrIN2JS18WasmModuleListenerEELm0EN2js17SystemAllocPolicyEED2Ev
_ZN7mozilla6VectorI6RefPtrIN2JS18WasmModuleListenerEELm0EN2js17SystemAllocPolicyEED4Ev
static void mozilla::detail::VectorImpl<T, N, AP, IsPod>::destroy(T*, T*) [with T = RefPtr<JS::WasmModuleListener>; long unsigned int N = 0ul; AP = js::SystemAllocPolicy; bool IsPod = false]
RefPtr<T>::~RefPtr() [with T = JS::WasmModuleListener]
_ZN6RefPtrIN2JS18WasmModuleListenerEED2Ev
_ZN6RefPtrIN2JS18WasmModuleListenerEED4Ev
static void RefPtr<T>::ConstRemovingRefPtrTraits<U>::Release(U*) [with U = JS::WasmModuleListener; T = JS::WasmModuleListener]
static void mozilla::RefPtrTraits<U>::Release(U*) [with U = JS::WasmModuleListener]
JS::WasmModuleListener.Release
IDBObjectStore.cpp:uint32 mozilla::dom::{anonymous}::WasmCompiledModuleStream::Release()
IDBObjectStore.cpp:void mozilla::dom::{anonymous}::WasmCompiledModuleStream::~WasmCompiledModuleStream()
_ZN7mozilla3dom12_GLOBAL__N_124WasmCompiledModuleStreamD1Ev
_ZN7mozilla3dom12_GLOBAL__N_124WasmCompiledModuleStreamD2Ev
IDBObjectStore.cpp:void mozilla::dom::{anonymous}::WasmCompiledModuleStream::~WasmCompiledModuleStream(int32)
IDBObjectStore.cpp:uint32 mozilla::dom::{anonymous}::WasmCompiledModuleStream::Close()
IDBObjectStore.cpp:uint32 mozilla::dom::{anonymous}::WasmCompiledModuleStream::CloseWithStatus(uint32)
nsIInputStream.Close
unresolved nsIInputStream.Close
Comment 1•6 years ago
|
||
(In reply to Steve Fink [:sfink] [:s:] from comment #0)
That's interesting. Can we end up doing a CC (and hence GC) when we release a reference? It does seem that this could cause us to run pretty much any code.
In this case, do we need to extract mWasmModule into a new separate RefPtr or can we just do file.mWasmModule->createObject(aCx)? I'm assuming that can't clear file.mWasmModule or something.
Assignee | ||
Comment 2•6 years ago
|
||
(In reply to Jon Coppeard (:jonco) from comment #1)
> (In reply to Steve Fink [:sfink] [:s:] from comment #0)
> That's interesting. Can we end up doing a CC (and hence GC) when we release
> a reference? It does seem that this could cause us to run pretty much any
> code.
>
> In this case, do we need to extract mWasmModule into a new separate RefPtr
> or can we just do file.mWasmModule->createObject(aCx)? I'm assuming that
> can't clear file.mWasmModule or something.
That's a great question! And I don't know the answer. I don't understand anything about RefPtr.
But if it did clear file.mWasmModule, I don't think it would be a problem. Or rather, if it were a problem, it would report a hazard in the code that did the clear. As long as we get the JSObject* back successfully, there's nothing else that will trigger a GC after the return value is placed on the stack.
baku, is the right fix to do file.mWasmModule->createObject(aCx) here, or if not, is there another option?
Oh... I guess one way to do this would be just
{
RefPtr<JS::WasmModule> module = file.mWasmModule;
JS::Rooted<JSObject*> wrappedModule(aCx, module->createObject(aCx));
if (NS_WARN_IF(!wrappedModule)) {
return nullptr;
}
result.set(wrappedModule);
}
return result;
Then ~RefPtr can GC all it wants while result is safely wrapped up in a Rooted. But I'm leaving the needinfo, as that seems simpler & cleaner if it's valid.
Flags: needinfo?(amarchesini)
Assignee | ||
Comment 3•6 years ago
|
||
Better than a needinfo, here's a review request. It seems to compile, and fixes the two hazards.
Attachment #8997576 -
Flags: review?(amarchesini)
Assignee | ||
Updated•6 years ago
|
Assignee: nobody → sphink
Status: NEW → ASSIGNED
Assignee | ||
Updated•6 years ago
|
Flags: needinfo?(amarchesini)
Updated•6 years ago
|
Attachment #8997576 -
Flags: review?(amarchesini) → review+
Pushed by sfink@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/8ac2b54e22a8
Fix hazard in CopyingStructuredCloneReadCallback, r=baku
Comment 5•6 years ago
|
||
bugherder |
Status: ASSIGNED → RESOLVED
Closed: 6 years ago
status-firefox63:
--- → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla63
Assignee | ||
Comment 6•6 years ago
|
||
Comment on attachment 8997576 [details] [diff] [review]
Fix hazard in CopyingStructuredCloneReadCallback
Approval Request Comment
[Feature/Bug causing the regression]: longstanding
[User impact if declined]: bug 1404274 is blocked from landing onto esr60. I don't know if that means we want to land this on beta to reduce the delta between beta and esr60?
[Is this code covered by automated tests?]: no
[Has the fix been verified in Nightly?]: it is in mozilla-central
[Needs manual test from QE? If yes, steps to reproduce]: no
[List of other uplifts needed for the feature/fix]: none
[Is the change risky?]: no
[Why is the change risky/not risky?]: it shifts around some operations slightly to remove a problematic series of actions that the rooting hazard analysis flags.
[String changes made/needed]: none
[esr60 Approval Request Comment]
If this is not a sec:{high,crit} bug, please state case for ESR consideration: needed for bug 1404274 landing (to avoid a rooting hazard)
User impact if declined: see other bug. But this cleans up some sketchiness as well -- I don't see how to trigger it, but if something JS::WasmModule::createObject were to cause the WasmModule to self-destruct, then this is a theoretical UAF-style rooting hazard. Like I said, I don't see how that would be actually possible, but something could be added in the future or maybe there's an angle to this that I'm not seeing.
Fix Landed on Version: 63
Risk to taking this patch (and alternatives if risky): very low risk. It's just shifting some operations around a little.
String or UUID changes made by this patch: none
Attachment #8997576 -
Flags: approval-mozilla-esr60?
Attachment #8997576 -
Flags: approval-mozilla-beta?
Comment on attachment 8997576 [details] [diff] [review]
Fix hazard in CopyingStructuredCloneReadCallback
Let's take this for beta - sounds like a good idea to keep 62 in sync with ESR here even if the issue isn't showing up obviously in 62 beta.
Attachment #8997576 -
Flags: approval-mozilla-beta? → approval-mozilla-beta+
Comment 8•6 years ago
|
||
bugherder uplift |
status-firefox62:
--- → fixed
Comment 9•6 years ago
|
||
Comment on attachment 8997576 [details] [diff] [review]
Fix hazard in CopyingStructuredCloneReadCallback
Needed to uplift bug 1404274 to ESR60. Approved for ESR 60.2.
Attachment #8997576 -
Flags: approval-mozilla-esr60? → approval-mozilla-esr60+
Comment 10•6 years ago
|
||
bugherder uplift |
status-firefox-esr60:
--- → fixed
Updated•6 years ago
|
You need to log in
before you can comment on or make changes to this bug.
Description
•