[Webkit-unassigned] [Bug 162702] New: Fix race condition in StringView's UnderlyingString lifecycle management.

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Wed Sep 28 14:04:01 PDT 2016


https://bugs.webkit.org/show_bug.cgi?id=162702

            Bug ID: 162702
           Summary: Fix race condition in StringView's UnderlyingString
                    lifecycle management.
    Classification: Unclassified
           Product: WebKit
           Version: WebKit Local Build
          Hardware: Unspecified
                OS: Unspecified
            Status: NEW
          Severity: Normal
          Priority: P2
         Component: Web Template Framework
          Assignee: webkit-unassigned at lists.webkit.org
          Reporter: mark.lam at apple.com

There 2 relevant functions at play:

void StringView::setUnderlyingString(const StringImpl* string)
{
    UnderlyingString* underlyingString;
    if (!string)
        underlyingString = nullptr;
    else {
        std::lock_guard<StaticLock> lock(underlyingStringsMutex);
        auto result = underlyingStrings().add(string, nullptr);
        if (result.isNewEntry)
            result.iterator->value = new UnderlyingString(*string);
        else
            ++result.iterator->value->refCount;
        underlyingString = result.iterator->value; // Point P2.
    }
    adoptUnderlyingString(underlyingString); // Point P5.
}

... and ...

void StringView::adoptUnderlyingString(UnderlyingString* underlyingString)
{
    if (m_underlyingString) {
        // Point P0.
        if (!--m_underlyingString->refCount) {
            if (m_underlyingString->isValid) { // Point P1.
                std::lock_guard<StaticLock> lock(underlyingStringsMutex);
                underlyingStrings().remove(&m_underlyingString->string); // Point P3.
            }
            delete m_underlyingString; // Point P4.
        }
    }
    m_underlyingString = underlyingString;
}

Imagine the following scenario:

1. Thread T1 has been using an UnderlyingString U1, and is now done with it.
   T1 runs up to point P1 in adoptUnderlyingString(), and is blocked waiting for
   the underlyingStringsMutex (which is currently being held by Thread T2).
2. Context switch to Thread T2.
   T2 wants to use UnderlyingString U1, and runs up to point P2 in setUnderlyingString()
   and releases the underlyingStringsMutex.
   Note: T2 thinks it has successfully refCounted U1, and therefore U1 is safe to use.
3. Context switch to Thread T1.
   T1 acquires the underlyingStringsMutex, and proceeds to remove it from the
   underlyingStrings() map (see Point P3).  It thinks it has successfully done so
   and proceeds to delete U1 (see Point P4).
4. Context switch to Thread T2.
   T2 proceeds to use U1 (see Point P5 in setUnderlyingString()).
   Note: U1 has already been freed.  This is a use after free.

The fix is to acquire the underlyingStringsMutex at Point P0 in adoptUnderlyingString() instead of after P1.  This ensures that the decrementing of the UnderlyingString refCount and its removal from the underlyingStrings() map is done as an atomic unit.

Note: If you look in StringView.cpp, you see another setUnderlyingString() which takes a StringView otherString.  This version of setUnderlyingString() can only be called from within the same thread that created the other StringView.  As a result, here, we are guaranteed that the UnderlyingString refCount is never zero, and there's no other threat of another thread trying to delete the UnderlyingString while we adopt it.  Hence, we don't need to acquire the underlyingStringsMutex here.

-- 
You are receiving this mail because:
You are the assignee for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.webkit.org/pipermail/webkit-unassigned/attachments/20160928/b704d575/attachment.html>


More information about the webkit-unassigned mailing list