Changeset 40208 in webkit for trunk/JavaScriptCore/runtime


Ignore:
Timestamp:
Jan 23, 2009, 7:03:25 PM (16 years ago)
Author:
[email protected]
Message:

2009-01-23 Gavin Barraclough <[email protected]>

Reviewed by Geoff Garen.

Fix for <rdar://problem/6126212>
Ensure that callbacks out from the JSC interface are only allowed
to return in reverse-chronological order to that in which they were
made. If we allow earlier callbacks to return first, then this may
result in setions of the RegisterFile in use by another thread
being trampled.

See uber-comment in JSLock.h for details.

  • runtime/JSLock.cpp: (JSC::JSLock::DropAllLocks::DropAllLocks): (JSC::JSLock::DropAllLocks::~DropAllLocks):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/runtime/JSLock.cpp

    r38528 r40208  
    120120}
    121121
     122// This is fairly nasty.  We allow multiple threads to run on the same
     123// context, and we do not require any locking semantics in doing so -
     124// clients of the API may simply use the context from multiple threads
     125// concurently, and assume this will work.  In order to make this work,
     126// We lock the context when a thread enters, and unlock it when it leaves.
     127// However we do not only unlock when the thread returns from its
     128// entry point (evaluate script or call function), we also unlock the
     129// context if the thread leaves JSC by making a call out to an external
     130// function through a callback.
     131//
     132// All threads using the context share the same JS stack (the RegisterFile).
     133// Whenever a thread calls into JSC it starts using the RegisterFile from the
     134// previous 'high water mark' - the maximum point the stack has ever grown to
     135// (returned by RegisterFile::end()).  So if a first thread calls out to a
     136// callback, and a second thread enters JSC, then also exits by calling out
     137// to a callback, we can be left with stackframes from both threads in the
     138// RegisterFile.  As such, a problem may occur should the first thread's
     139// callback complete first, and attempt to return to JSC.  Were we to allow
     140// this to happen, and were its stack to grow further, then it may potentially
     141// write over the second thread's call frames.
     142//
     143// In avoid JS stack corruption we enforce a policy of only ever allowing two
     144// threads to use a JS context concurrently, and only allowing the second of
     145// these threads to execute until it has completed and fully returned from its
     146// outermost call into JSC.  We enforce this policy using 'lockDropDepth'.  The
     147// first time a thread exits it will call DropAllLocks - which will do as expected
     148// and drop locks allowing another thread to enter.  Should another thread, or the
     149// same thread again, enter JSC (through evaluate script or call function), and exit
     150// again through a callback, then the locks will not be dropped when DropAllLocks
     151// is called (since lockDropDepth is non-zero).  Since this thread is still holding
     152// the locks, only it will re able to re-enter JSC (either be returning from the
     153// callback, or by re-entering through another call to evaulate script or call
     154// function).
     155//
     156// This policy is slightly more restricive than it needs to be for correctness -
     157// we could validly allow futher entries into JSC from other threads, we only
     158// need ensure that callbacks return in the reverse chronological order of the
     159// order in which they were made - though implementing the less restrictive policy
     160// would likely increase complexity and overhead.
     161//
     162static unsigned lockDropDepth = 0;
     163
    122164JSLock::DropAllLocks::DropAllLocks(ExecState* exec)
    123165    : m_lockingForReal(exec->globalData().isSharedInstance)
    124166{
    125167    pthread_once(&createJSLockCountOnce, createJSLockCount);
     168
     169    if (lockDropDepth++) {
     170        m_lockCount = 0;
     171        return;
     172    }
    126173
    127174    m_lockCount = JSLock::lockCount();
     
    135182    pthread_once(&createJSLockCountOnce, createJSLockCount);
    136183
     184    if (lockDropDepth++) {
     185        m_lockCount = 0;
     186        return;
     187    }
     188
    137189    // It is necessary to drop even "unreal" locks, because having a non-zero lock count
    138190    // will prevent a real lock from being taken.
     
    147199    for (intptr_t i = 0; i < m_lockCount; i++)
    148200        JSLock::lock(m_lockingForReal);
     201
     202    --lockDropDepth;
    149203}
    150204
Note: See TracChangeset for help on using the changeset viewer.