Title: [122494] trunk/Source/WebCore
Revision
122494
Author
[email protected]
Date
2012-07-12 12:59:23 -0700 (Thu, 12 Jul 2012)

Log 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.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (122493 => 122494)


--- trunk/Source/WebCore/ChangeLog	2012-07-12 19:49:33 UTC (rev 122493)
+++ trunk/Source/WebCore/ChangeLog	2012-07-12 19:59:23 UTC (rev 122494)
@@ -1,3 +1,32 @@
+2012-07-12  Gavin Barraclough  <[email protected]>
+
+        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.
+
 2012-07-11  David Hyatt  <[email protected]>
 
         https://bugs.webkit.org/show_bug.cgi?id=91000

Modified: trunk/Source/WebCore/bindings/objc/WebScriptObject.mm (122493 => 122494)


--- trunk/Source/WebCore/bindings/objc/WebScriptObject.mm	2012-07-12 19:49:33 UTC (rev 122493)
+++ trunk/Source/WebCore/bindings/objc/WebScriptObject.mm	2012-07-12 19:59:23 UTC (rev 122494)
@@ -50,6 +50,7 @@
 #import <runtime/JSLock.h>
 #import <runtime/Completion.h>
 #import <runtime/Completion.h>
+#import <wtf/TCSpinLock.h>
 #import <wtf/Threading.h>
 
 
@@ -60,16 +61,24 @@
 namespace WebCore {
 
 static NSMapTable* JSWrapperCache;
+static SpinLock spinLock = SPINLOCK_INITIALIZER;
 
 NSObject* getJSWrapper(JSObject* impl)
 {
+    ASSERT(isMainThread());
+    SpinLockHolder holder(&spinLock);
+
     if (!JSWrapperCache)
         return nil;
-    return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
+    NSObject* wrapper = static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
+    return wrapper ? [[wrapper retain] autorelease] : nil;
 }
 
 void addJSWrapper(NSObject* wrapper, JSObject* impl)
 {
+    ASSERT(isMainThread());
+    SpinLockHolder holder(&spinLock);
+
     if (!JSWrapperCache)
         JSWrapperCache = createWrapperCache();
     NSMapInsert(JSWrapperCache, impl, wrapper);
@@ -77,18 +86,27 @@
 
 void removeJSWrapper(JSObject* impl)
 {
+    SpinLockHolder holder(&spinLock);
+
     if (!JSWrapperCache)
         return;
     NSMapRemove(JSWrapperCache, impl);
 }
 
+static void removeJSWrapperIfRetainCountOne(NSObject* wrapper, JSObject* impl)
+{
+    SpinLockHolder holder(&spinLock);
+
+    if (!JSWrapperCache)
+        return;
+    if ([wrapper retainCount] == 1)
+        NSMapRemove(JSWrapperCache, impl);
+}
+
 id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
 {
-    // NSMap is not thread safe, hold the JSC API lock; also synchronize this vs. release.
-    JSC::JSLockHolder holder(JSDOMWindowBase::commonJSGlobalData());
-
     if (id wrapper = getJSWrapper(object))
-        return [[wrapper retain] autorelease];
+        return wrapper;
     return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
 }
 
@@ -148,9 +166,6 @@
     _private->rootObject = rootObject.leakRef();
     _private->originRootObject = originRootObject.leakRef();
 
-    // NSMap is not thread safe, hold the JSC API lock.
-    JSC::JSLockHolder holder(JSDOMWindowBase::commonJSGlobalData());
-
     WebCore::addJSWrapper(self, imp);
 
     if (_private->rootObject)
@@ -231,16 +246,10 @@
 
 - (oneway void)release
 {
-    {
-        // NSMap is not thread safe, hold the JSC API lock; also synchronize this vs. getJSWrapper.
-        JSC::JSLockHolder holder(JSDOMWindowBase::commonJSGlobalData());
+    // If we're releasing the last reference to this object, remove if from the map.
+    if (_private->imp)
+        WebCore::removeJSWrapperIfRetainCountOne(self, _private->imp);
 
-        // If we're releasing the last reference to this object, remove if from the map,
-        // this will prevent this object from being returned by getJSWrapper.
-        if (_private->imp && [self retainCount] == 1)
-            WebCore::removeJSWrapper(_private->imp);
-    }
-
     [super release];
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to