Changeset 180602 in webkit


Ignore:
Timestamp:
Feb 24, 2015 6:28:23 PM (9 years ago)
Author:
mark.lam@apple.com
Message:

MachineThreads::Thread clean up has a use after free race condition.
<https://webkit.org/b/141990>

Reviewed by Michael Saboff.

MachineThreads::Thread clean up relies on the clean up mechanism
implemented in _pthread_tsd_cleanup_key(), which looks like this:

void _pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key)
{

void (*destructor)(void *);
if (_pthread_key_get_destructor(key, &destructor)) {

void ptr = &self->tsd[key];
void *value = *ptr;

At this point, this thread has cached "destructor" and "value"
(which is a MachineThreads*). If the VM gets destructed (along
with its MachineThreads registry) by another thread, then this
thread will have no way of knowing that the MachineThreads* is
now pointing to freed memory. Calling the destructor below will
therefore result in a use after free scenario when it tries to
access the MachineThreads' data members.

if (value) {

*ptr = NULL;
if (destructor) {

destructor(value);

}

}

}

}

The solution is simply to change MachineThreads from a per VM thread
registry to a process global singleton thread registry i.e. the
MachineThreads registry is now immortal and we cannot have a use after
free scenario since we never free it.

The cost of this change is that all VM instances will have to scan
stacks of all threads ever touched by a VM, and not just those that
touched a specific VM. However, stacks tend to be shallow. Hence,
those additional scans will tend to be cheap.

Secondly, it is not common for there to be multiple JSC VMs in use
concurrently on multiple threads. Hence, this cost should rarely
manifest in real world applications.

  • heap/Heap.cpp:

(JSC::Heap::Heap):
(JSC::Heap::machineThreads):
(JSC::Heap::gatherStackRoots):

  • heap/Heap.h:

(JSC::Heap::machineThreads): Deleted.

  • heap/MachineStackMarker.cpp:

(JSC::MachineThreads::MachineThreads):
(JSC::MachineThreads::~MachineThreads):
(JSC::MachineThreads::addCurrentThread):

  • heap/MachineStackMarker.h:
  • runtime/JSLock.cpp:

(JSC::JSLock::didAcquireLock):

Location:
trunk/Source/JavaScriptCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r180599 r180602  
     12015-02-24  Mark Lam  <mark.lam@apple.com>
     2
     3        MachineThreads::Thread clean up has a use after free race condition.
     4        <https://webkit.org/b/141990>
     5
     6        Reviewed by Michael Saboff.
     7
     8        MachineThreads::Thread clean up relies on the clean up mechanism
     9        implemented in _pthread_tsd_cleanup_key(), which looks like this:
     10
     11        void _pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key)
     12        {
     13            void (*destructor)(void *);
     14            if (_pthread_key_get_destructor(key, &destructor)) {
     15                void **ptr = &self->tsd[key];
     16                void *value = *ptr;
     17
     18                // At this point, this thread has cached "destructor" and "value"
     19                // (which is a MachineThreads*).  If the VM gets destructed (along
     20                // with its MachineThreads registry) by another thread, then this
     21                // thread will have no way of knowing that the MachineThreads* is
     22                // now pointing to freed memory.  Calling the destructor below will
     23                // therefore result in a use after free scenario when it tries to
     24                // access the MachineThreads' data members.
     25
     26                if (value) {
     27                    *ptr = NULL;
     28                    if (destructor) {
     29                        destructor(value);
     30                    }
     31                }
     32            }
     33        }
     34
     35        The solution is simply to change MachineThreads from a per VM thread
     36        registry to a process global singleton thread registry i.e. the
     37        MachineThreads registry is now immortal and we cannot have a use after
     38        free scenario since we never free it.
     39
     40        The cost of this change is that all VM instances will have to scan
     41        stacks of all threads ever touched by a VM, and not just those that
     42        touched a specific VM.  However, stacks tend to be shallow.  Hence,
     43        those additional scans will tend to be cheap.
     44
     45        Secondly, it is not common for there to be multiple JSC VMs in use
     46        concurrently on multiple threads.  Hence, this cost should rarely
     47        manifest in real world applications.
     48
     49        * heap/Heap.cpp:
     50        (JSC::Heap::Heap):
     51        (JSC::Heap::machineThreads):
     52        (JSC::Heap::gatherStackRoots):
     53        * heap/Heap.h:
     54        (JSC::Heap::machineThreads): Deleted.
     55        * heap/MachineStackMarker.cpp:
     56        (JSC::MachineThreads::MachineThreads):
     57        (JSC::MachineThreads::~MachineThreads):
     58        (JSC::MachineThreads::addCurrentThread):
     59        * heap/MachineStackMarker.h:
     60        * runtime/JSLock.cpp:
     61        (JSC::JSLock::didAcquireLock):
     62
    1632015-02-24  Myles C. Maxfield  <mmaxfield@apple.com>
    264
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r180591 r180602  
    312312    , m_storageSpace(this)
    313313    , m_extraMemoryUsage(0)
    314     , m_machineThreads(this)
    315314    , m_sharedData(vm)
    316315    , m_slotVisitor(m_sharedData)
     
    350349}
    351350
     351MachineThreads& Heap::machineThreads()
     352{
     353    static std::once_flag initializeMachineThreadsOnceFlag;
     354    static MachineThreads* machineThreads = nullptr;
     355
     356    std::call_once(initializeMachineThreadsOnceFlag, [] {
     357        machineThreads = new MachineThreads();
     358    });
     359
     360    return *machineThreads;
     361}
     362   
    352363bool Heap::isPagedOut(double deadline)
    353364{
     
    588599    GCPHASE(GatherStackRoots);
    589600    m_jitStubRoutines.clearMarks();
    590     m_machineThreads.gatherConservativeRoots(roots, m_jitStubRoutines, m_codeBlocks, dummy, registers);
     601    machineThreads().gatherConservativeRoots(roots, m_jitStubRoutines, m_codeBlocks, dummy, registers);
    591602}
    592603
  • trunk/Source/JavaScriptCore/heap/Heap.h

    r180591 r180602  
    120120    VM* vm() const { return m_vm; }
    121121    MarkedSpace& objectSpace() { return m_objectSpace; }
    122     MachineThreads& machineThreads() { return m_machineThreads; }
     122    static MachineThreads& machineThreads();
    123123
    124124    const SlotVisitor& slotVisitor() const { return m_slotVisitor; }
     
    356356    std::unique_ptr<HashSet<MarkedArgumentBuffer*>> m_markListSet;
    357357
    358     MachineThreads m_machineThreads;
    359    
    360358    GCThreadSharedData m_sharedData;
    361359    SlotVisitor m_slotVisitor;
  • trunk/Source/JavaScriptCore/heap/MachineStackMarker.cpp

    r180591 r180602  
    116116};
    117117
    118 MachineThreads::MachineThreads(Heap* heap)
     118MachineThreads::MachineThreads()
    119119    : m_registeredThreads(0)
    120120    , m_threadSpecific(0)
    121 #if !ASSERT_DISABLED
    122     , m_heap(heap)
    123 #endif
    124 {
    125     UNUSED_PARAM(heap);
     121{
    126122    threadSpecificKeyCreate(&m_threadSpecific, removeThread);
    127123}
    128124
    129 MachineThreads::~MachineThreads()
    130 {
    131     threadSpecificKeyDelete(m_threadSpecific);
    132 
    133     MutexLocker registeredThreadsLock(m_registeredThreadsMutex);
    134     for (Thread* t = m_registeredThreads; t;) {
    135         Thread* next = t->next;
    136         delete t;
    137         t = next;
    138     }
     125NO_RETURN_DUE_TO_CRASH MachineThreads::~MachineThreads()
     126{
     127    RELEASE_ASSERT_NOT_REACHED();
    139128}
    140129
     
    161150}
    162151
    163 void MachineThreads::addCurrentThread()
    164 {
    165     ASSERT(!m_heap->vm()->hasExclusiveThread() || m_heap->vm()->exclusiveThread() == std::this_thread::get_id());
     152void MachineThreads::addCurrentThread(VM* vm)
     153{
     154    ASSERT_UNUSED(vm, !vm->hasExclusiveThread() || vm->exclusiveThread() == std::this_thread::get_id());
    166155
    167156    if (threadSpecificGet(m_threadSpecific)) {
  • trunk/Source/JavaScriptCore/heap/MachineStackMarker.h

    r180591 r180602  
    3434    class Heap;
    3535    class JITStubRoutineSet;
     36    class VM;
    3637
    3738    class MachineThreads {
     
    4041        typedef jmp_buf RegisterState;
    4142
    42         MachineThreads(Heap*);
     43        MachineThreads();
    4344        ~MachineThreads();
    4445
    4546        void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, void* stackCurrent, RegisterState& registers);
    4647
    47         JS_EXPORT_PRIVATE void addCurrentThread(); // Only needs to be called by clients that can use the same heap from multiple threads.
     48        JS_EXPORT_PRIVATE void addCurrentThread(VM*); // Only needs to be called by clients that can use the same heap from multiple threads.
    4849
    4950    private:
     
    6162        Thread* m_registeredThreads;
    6263        WTF::ThreadSpecificKey m_threadSpecific;
    63 #if !ASSERT_DISABLED
    64         Heap* m_heap;
    65 #endif
    6664    };
    6765
  • trunk/Source/JavaScriptCore/runtime/JSLock.cpp

    r179728 r180602  
    145145    ASSERT(m_entryAtomicStringTable);
    146146
    147     m_vm->heap.machineThreads().addCurrentThread();
     147    m_vm->heap.machineThreads().addCurrentThread(m_vm);
    148148}
    149149
Note: See TracChangeset for help on using the changeset viewer.