StaticRefPtr<> can cause UAFs if used with concurrent threads
Categories
(Core :: XPCOM, defect)
Tracking
()
People
(Reporter: mozillabugs, Unassigned)
References
Details
(Keywords: csectype-race, reporter-external, sec-audit)
Attachments
(3 files)
StaticRefPtr<> (xpcom/base/StaticPtr.h) typically is used in a manner that, if concurrent threads are in play, could cause UAFs. A typical usage is:
class someClass {
...
static StaticRefPtr<someClass> gSingleton
...
static someClass * GetSingleton() {
if (!gSingleton) {
gSingleton = new someClass(...);
}
return gSingleton;
}
...
};
Now imagine that GetSingleton() is called in 2 threads concurrently, and that both both find the !singleton condition true and execute the new someClass, creating objects o1 and o2. Then:
thread 1 thread 2
-------- --------
StaticRefPtr::operator=(&o1) { StaticRefPtr::operator=(&o2) {
AssignWithAddref(aRhs); AssignWithAddref(aRhs);
StaticRefPtr::AssignWithAddref(aNewPtr) { StaticRefPtr::AssignWithAddref(aNewPtr) {
...AddRef(aNewPtr); ...AddRef(aNewPtr);
AssignAssumingAddref(aNewPtr); AssignAssumingAddref(&aNewPtr);
StaticRefPtr::AssignAssumingAddref(aNewPtr) {
T *oldPtr = mRawPtr; // NULL
mRawPtr = aNewPtr; // &o1
if (oldPtr) { // not taken
...
}
return gSingleton; // return &o1
...
StaticRefPtr::AssignAssumingAddref(aNewPtr) {
T *oldPtr = mRawPtr; // &o1
mRawPtr = aNewPtr; // &o2
if (oldPtr) { // taken
Release(oldPtr); // &o1, refcnt is now 0
// so it deletes &o1
GetSingleton() -> oh_oh(); // uses &o1 => UAF!
Attached is a test program (using a simplified version of StaticRefPtr to make it easy to build) that demonstrates the issue. To use it:
- Build it as a debug-mode console program in VS 2022.
- Set a BP on the calls to
GetSingleton()inthread1f()andthread2f(). - Run the program.
- The BP in
thread1f()should fire. If not, run the program again. - When the BP fires, freeze all but the firing thread (thread 1) and step into the
GetSingleton()call. Step to, but not farther than, thenewstatement. Now freeze this thread, thaw the other threads, and proceed. - When the BP in
thread2f()fires (in thread 2), step up to, but not including the call toStaticRefPtr::AssignAssumingAddRef(). Now freeze this thread and thaw the other threads. - Use the debugger to switch to thread 1 (the one with
thread1f()on its stack). - Step through the
return mpSingletonand back intothread1f(), and then up to, but not into, the call toCheckForPuddyTat(). RecordmpSingleton->mRawPtr. - Freeze thread 1, thaw the other threads, and use the debugger to switch to thread 2 (the one with
thread2f()on its stack). - Now step
AssignAssumingAddRef()in thread 2. Notice how it callsRelease()on the existingmpSingleton->mRawPtr, and then deletes it. - Freeze thread 2, thaw the other threads, and switch to thread 1.
- Examine
p. Notice that it contains the address of the object that got deleted in step 10, which is the same address you recorded in step 8. - Step into the call to
CheckForPuddyTat(). Observe that the test fori != 0et al succeeds and the program prints a warning.
The issue is that the test and assignment of gSingleton are not atomic. It might take a bit of trickery to make them atomic, to avoid unnecessary constructor calls, and to avoid unintuitive usages. Maybe variadic templates and compare_exchange_strong()?
I will try to find a case in which FF actually can experience this bug.
Updated•2 years ago
|
Comment 1•2 years ago
|
||
I think this is a known issue, or at least I was aware of it. Any kind of RefPtr is going to have the same issue. Generally these kinds of static variables are initialized on the main thread from startup, or consistently on some other thread. TSan will yell at us if somebody is doing things wrong, but it is possible we are missing it somewhere.
| Reporter | ||
Comment 2•2 years ago
•
|
||
| Reporter | ||
Comment 3•2 years ago
•
|
||
| Reporter | ||
Comment 4•2 years ago
|
||
There might be another unfixed bug with TSAN flagging StaticRefPtr: https://bugzilla.mozilla.org/show_bug.cgi?id=1717134 .
Comment 5•2 years ago
|
||
The severity field is not set for this bug.
:nika, could you have a look please?
For more information, please visit BugBot documentation.
Updated•2 years ago
|
Updated•1 year ago
|
Updated•1 year ago
|
Description
•