Changeset 122494 in webkit


Ignore:
Timestamp:
Jul 12, 2012 12:59:23 PM (12 years ago)
Author:
barraclough@apple.com
Message:

Threadsafety issues in WebScriptObject
https://bugs.webkit.org/show_bug.cgi?id=90849

Reviewed by Filip Pizlo & Oliver Hunt.

Updated fix for this bug. Taking the JSC API lock from WebScriptObject::release
may not be safe; better to just guard the JSWrapperCache with its own spinlock.

  • bindings/objc/WebScriptObject.mm:

(WebCore::getJSWrapper):

  • Added spinlock; also retain/autorelease the returned wrapper - it is unsafe to wait for the caller to do so, due to a race condition vs release removing the wrapper from the map.

(WebCore::addJSWrapper):

  • Take the spinlock guarding the cache.

(WebCore::removeJSWrapper):

  • Take the spinlock guarding the cache.

(WebCore::removeJSWrapperIfRetainCountOne):

  • Take the spinlock guarding the cache, remove the wrapper if retainCount is one.

(WebCore::createJSWrapper):

  • Remove the API lock; this method no longer needs to retain/autorelease (this is done by getJSWrapper).

(-[WebScriptObject _setImp:originRootObject:rootObject:]):

  • Remove the API lock.

(-[WebScriptObject release]):

  • Remove the API lock, retainCount check moved into removeJSWrapperIfRetainCountOne.
Location:
trunk/Source/WebCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r122489 r122494  
     12012-07-12  Gavin Barraclough  <barraclough@apple.com>
     2
     3        Threadsafety issues in WebScriptObject
     4        https://bugs.webkit.org/show_bug.cgi?id=90849
     5
     6        Reviewed by Filip Pizlo & Oliver Hunt.
     7
     8        Updated fix for this bug. Taking the JSC API lock from WebScriptObject::release
     9        may not be safe; better to just guard the JSWrapperCache with its own spinlock.
     10
     11        * bindings/objc/WebScriptObject.mm:
     12        (WebCore::getJSWrapper):
     13            - Added spinlock; also retain/autorelease the returned wrapper - it is unsafe
     14              to wait for the caller to do so, due to a race condition vs release removing
     15              the wrapper from the map.
     16        (WebCore::addJSWrapper):
     17            - Take the spinlock guarding the cache.
     18        (WebCore::removeJSWrapper):
     19            - Take the spinlock guarding the cache.
     20        (WebCore::removeJSWrapperIfRetainCountOne):
     21            - Take the spinlock guarding the cache, remove the wrapper if retainCount is one.
     22        (WebCore::createJSWrapper):
     23            - Remove the API lock; this method no longer needs to retain/autorelease (this is
     24              done by getJSWrapper).
     25        (-[WebScriptObject _setImp:originRootObject:rootObject:]):
     26            - Remove the API lock.
     27        (-[WebScriptObject release]):
     28            - Remove the API lock, retainCount check moved into removeJSWrapperIfRetainCountOne.
     29
    1302012-07-11  David Hyatt  <hyatt@apple.com>
    231
  • trunk/Source/WebCore/bindings/objc/WebScriptObject.mm

    r122198 r122494  
    5151#import <runtime/Completion.h>
    5252#import <runtime/Completion.h>
     53#import <wtf/TCSpinLock.h>
    5354#import <wtf/Threading.h>
    5455
     
    6162
    6263static NSMapTable* JSWrapperCache;
     64static SpinLock spinLock = SPINLOCK_INITIALIZER;
    6365
    6466NSObject* getJSWrapper(JSObject* impl)
    6567{
     68    ASSERT(isMainThread());
     69    SpinLockHolder holder(&spinLock);
     70
    6671    if (!JSWrapperCache)
    6772        return nil;
    68     return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
     73    NSObject* wrapper = static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
     74    return wrapper ? [[wrapper retain] autorelease] : nil;
    6975}
    7076
    7177void addJSWrapper(NSObject* wrapper, JSObject* impl)
    7278{
     79    ASSERT(isMainThread());
     80    SpinLockHolder holder(&spinLock);
     81
    7382    if (!JSWrapperCache)
    7483        JSWrapperCache = createWrapperCache();
     
    7887void removeJSWrapper(JSObject* impl)
    7988{
     89    SpinLockHolder holder(&spinLock);
     90
    8091    if (!JSWrapperCache)
    8192        return;
     
    8394}
    8495
     96static void removeJSWrapperIfRetainCountOne(NSObject* wrapper, JSObject* impl)
     97{
     98    SpinLockHolder holder(&spinLock);
     99
     100    if (!JSWrapperCache)
     101        return;
     102    if ([wrapper retainCount] == 1)
     103        NSMapRemove(JSWrapperCache, impl);
     104}
     105
    85106id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
    86107{
    87     // NSMap is not thread safe, hold the JSC API lock; also synchronize this vs. release.
    88     JSC::JSLockHolder holder(JSDOMWindowBase::commonJSGlobalData());
    89 
    90108    if (id wrapper = getJSWrapper(object))
    91         return [[wrapper retain] autorelease];
     109        return wrapper;
    92110    return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
    93111}
     
    149167    _private->originRootObject = originRootObject.leakRef();
    150168
    151     // NSMap is not thread safe, hold the JSC API lock.
    152     JSC::JSLockHolder holder(JSDOMWindowBase::commonJSGlobalData());
    153 
    154169    WebCore::addJSWrapper(self, imp);
    155170
     
    232247- (oneway void)release
    233248{
    234     {
    235         // NSMap is not thread safe, hold the JSC API lock; also synchronize this vs. getJSWrapper.
    236         JSC::JSLockHolder holder(JSDOMWindowBase::commonJSGlobalData());
    237 
    238         // If we're releasing the last reference to this object, remove if from the map,
    239         // this will prevent this object from being returned by getJSWrapper.
    240         if (_private->imp && [self retainCount] == 1)
    241             WebCore::removeJSWrapper(_private->imp);
    242     }
     249    // If we're releasing the last reference to this object, remove if from the map.
     250    if (_private->imp)
     251        WebCore::removeJSWrapperIfRetainCountOne(self, _private->imp);
    243252
    244253    [super release];
Note: See TracChangeset for help on using the changeset viewer.