[JSC] DFG::CommonData modification by DFG reallyAdd should be guarded by CodeBlock's lock
https://bugs.webkit.org/show_bug.cgi?id=203177

Reviewed by Mark Lam.

JSTests:

* stress/dfg-really-add-locking.js: Added.

Source/JavaScriptCore:

When doing DFG reallyAdd, DFG::JITCode is already set in CodeBlock and DFG::CommonData can be
reachable from CodeBlock. So concurrent collector can trace entries of DFG::CommonData while DFG reallyAdd
is modifying it. It would be possible that we install DFG::JITCode after performing DFG reallyAdd, but for now,
we just protect DFG reallyAdd's DFG::CommonData modification by CodeBlock's lock so that concurrent collector
does not trace them in a racy manner.

* dfg/DFGDesiredGlobalProperties.cpp:
(JSC::DFG::DesiredGlobalProperties::reallyAdd):
* dfg/DFGDesiredIdentifiers.cpp:
(JSC::DFG::DesiredIdentifiers::reallyAdd):
* dfg/DFGDesiredTransitions.cpp:
(JSC::DFG::DesiredTransition::reallyAdd):
* dfg/DFGDesiredWatchpoints.cpp:
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::add):
(JSC::DFG::SymbolTableAdaptor::add):
(JSC::DFG::FunctionExecutableAdaptor::add):
(JSC::DFG::AdaptiveStructureWatchpointAdaptor::add):
* dfg/DFGDesiredWatchpoints.h:
(JSC::DFG::SetPointerAdaptor::add):
* dfg/DFGDesiredWeakReferences.cpp:
(JSC::DFG::DesiredWeakReferences::reallyAdd):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251321 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog
index 5273355..87272cc 100644
--- a/JSTests/ChangeLog
+++ b/JSTests/ChangeLog
@@ -1,3 +1,12 @@
+2019-10-18  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] DFG::CommonData modification by DFG reallyAdd should be guarded by CodeBlock's lock
+        https://bugs.webkit.org/show_bug.cgi?id=203177
+
+        Reviewed by Mark Lam.
+
+        * stress/dfg-really-add-locking.js: Added.
+
 2019-10-17  Mark Lam  <mark.lam@apple.com>
 
         Add missing checks after calls to the sameValue() JSValue comparator.
diff --git a/JSTests/stress/dfg-really-add-locking.js b/JSTests/stress/dfg-really-add-locking.js
new file mode 100644
index 0000000..901620b
--- /dev/null
+++ b/JSTests/stress/dfg-really-add-locking.js
@@ -0,0 +1,41 @@
+//@ slow!
+//@ runDefault("--collectContinuously=1", "--useGenerationalGC=0")
+
+for (var i = 0; i < 10; ++i) {
+    runString(`
+        var g;
+        (function () {
+            for (var i = 0; i < 100000; ++i) {
+                var o = {};
+                o.a = 0;
+                o.b = 1;
+                o.c = 2;
+                o.d = 3;
+                o.e = 4;
+                o.f = 5;
+                o.g = 6;
+                o.h = 7;
+                o.i = 8;
+                o.j = 9;
+                o.k = 10;
+                o.l = 11;
+                o.m = 12;
+                o.n = 13;
+                o.o = 14;
+                o.p = 15;
+                o.q = 0;
+                o.r = 1;
+                o.s = 2;
+                o.t = 3;
+                o.u = 4;
+                o.v = 5;
+                o.w = 6;
+                o.x = 7;
+                o.y = 8;
+                o.z = 9;
+                g = o;
+            }
+            return g;
+        }());
+    `);
+}
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 7a899b6..753f3bc 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,5 +1,34 @@
 2019-10-18  Yusuke Suzuki  <ysuzuki@apple.com>
 
+        [JSC] DFG::CommonData modification by DFG reallyAdd should be guarded by CodeBlock's lock
+        https://bugs.webkit.org/show_bug.cgi?id=203177
+
+        Reviewed by Mark Lam.
+
+        When doing DFG reallyAdd, DFG::JITCode is already set in CodeBlock and DFG::CommonData can be
+        reachable from CodeBlock. So concurrent collector can trace entries of DFG::CommonData while DFG reallyAdd
+        is modifying it. It would be possible that we install DFG::JITCode after performing DFG reallyAdd, but for now,
+        we just protect DFG reallyAdd's DFG::CommonData modification by CodeBlock's lock so that concurrent collector
+        does not trace them in a racy manner.
+
+        * dfg/DFGDesiredGlobalProperties.cpp:
+        (JSC::DFG::DesiredGlobalProperties::reallyAdd):
+        * dfg/DFGDesiredIdentifiers.cpp:
+        (JSC::DFG::DesiredIdentifiers::reallyAdd):
+        * dfg/DFGDesiredTransitions.cpp:
+        (JSC::DFG::DesiredTransition::reallyAdd):
+        * dfg/DFGDesiredWatchpoints.cpp:
+        (JSC::DFG::ArrayBufferViewWatchpointAdaptor::add):
+        (JSC::DFG::SymbolTableAdaptor::add):
+        (JSC::DFG::FunctionExecutableAdaptor::add):
+        (JSC::DFG::AdaptiveStructureWatchpointAdaptor::add):
+        * dfg/DFGDesiredWatchpoints.h:
+        (JSC::DFG::SetPointerAdaptor::add):
+        * dfg/DFGDesiredWeakReferences.cpp:
+        (JSC::DFG::DesiredWeakReferences::reallyAdd):
+
+2019-10-18  Yusuke Suzuki  <ysuzuki@apple.com>
+
         [JSC] Make ConcurrentJSLock Lock even if ENABLE_CONCURRENT_JS=OFF
         https://bugs.webkit.org/show_bug.cgi?id=202892
 
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp
index ba6841d..6416449 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDesiredGlobalProperties.cpp
@@ -61,7 +61,12 @@
         auto* uid = identifiers.at(property.identifierNumber());
         auto& watchpointSet = property.globalObject()->ensureReferencedPropertyWatchpointSet(uid);
         ASSERT(watchpointSet.isStillValid());
-        watchpointSet.add(common.watchpoints.add(codeBlock));
+        CodeBlockJettisoningWatchpoint* watchpoint = nullptr;
+        {
+            ConcurrentJSLocker locker(codeBlock->m_lock);
+            watchpoint = common.watchpoints.add(codeBlock);
+        }
+        watchpointSet.add(WTFMove(watchpoint));
     }
 }
 
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredIdentifiers.cpp b/Source/JavaScriptCore/dfg/DFGDesiredIdentifiers.cpp
index 77c0eb0..be35647 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredIdentifiers.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDesiredIdentifiers.cpp
@@ -89,7 +89,11 @@
 {
     for (auto rep : m_addedIdentifiers) {
         ASSERT(rep->hasAtLeastOneRef());
-        commonData->dfgIdentifiers.append(Identifier::fromUid(vm, rep));
+        Identifier uid = Identifier::fromUid(vm, rep);
+        {
+            ConcurrentJSLocker locker(m_codeBlock->m_lock);
+            commonData->dfgIdentifiers.append(WTFMove(uid));
+        }
     }
 }
 
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredTransitions.cpp b/Source/JavaScriptCore/dfg/DFGDesiredTransitions.cpp
index 4b82b03..0910ba8 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredTransitions.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDesiredTransitions.cpp
@@ -44,6 +44,7 @@
 
 void DesiredTransition::reallyAdd(VM& vm, CommonData* common)
 {
+    ConcurrentJSLocker locker(m_codeBlock->m_lock);
     common->transitions.append(
         WeakReferenceTransition(
             vm, m_codeBlock,
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
index 64cd3d2..c2431f0 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
@@ -39,10 +39,14 @@
     CodeBlock* codeBlock, JSArrayBufferView* view, CommonData& common)
 {
     VM& vm = codeBlock->vm();
-    Watchpoint* watchpoint = common.watchpoints.add(codeBlock);
+    CodeBlockJettisoningWatchpoint* watchpoint = nullptr;
+    {
+        ConcurrentJSLocker locker(codeBlock->m_lock);
+        watchpoint = common.watchpoints.add(codeBlock);
+    }
     ArrayBufferNeuteringWatchpointSet* neuteringWatchpoint =
         ArrayBufferNeuteringWatchpointSet::create(vm);
-    neuteringWatchpoint->set().add(watchpoint);
+    neuteringWatchpoint->set().add(WTFMove(watchpoint));
     codeBlock->addConstant(ConcurrentJSLocker(codeBlock->m_lock), neuteringWatchpoint);
     // FIXME: We don't need to set this watchpoint at all for shared buffers.
     // https://bugs.webkit.org/show_bug.cgi?id=164108
@@ -53,14 +57,24 @@
     CodeBlock* codeBlock, SymbolTable* symbolTable, CommonData& common)
 {
     codeBlock->addConstant(ConcurrentJSLocker(codeBlock->m_lock), symbolTable); // For common users, it doesn't really matter if it's weak or not. If references to it go away, we go away, too.
-    symbolTable->singleton().add(common.watchpoints.add(codeBlock));
+    CodeBlockJettisoningWatchpoint* watchpoint = nullptr;
+    {
+        ConcurrentJSLocker locker(codeBlock->m_lock);
+        watchpoint = common.watchpoints.add(codeBlock);
+    }
+    symbolTable->singleton().add(WTFMove(watchpoint));
 }
 
 void FunctionExecutableAdaptor::add(
     CodeBlock* codeBlock, FunctionExecutable* executable, CommonData& common)
 {
     codeBlock->addConstant(ConcurrentJSLocker(codeBlock->m_lock), executable); // For common users, it doesn't really matter if it's weak or not. If references to it go away, we go away, too.
-    executable->singleton().add(common.watchpoints.add(codeBlock));
+    CodeBlockJettisoningWatchpoint* watchpoint = nullptr;
+    {
+        ConcurrentJSLocker locker(codeBlock->m_lock);
+        watchpoint = common.watchpoints.add(codeBlock);
+    }
+    executable->singleton().add(WTFMove(watchpoint));
 }
 
 void AdaptiveStructureWatchpointAdaptor::add(
@@ -68,13 +82,25 @@
 {
     VM& vm = codeBlock->vm();
     switch (key.kind()) {
-    case PropertyCondition::Equivalence:
-        common.adaptiveInferredPropertyValueWatchpoints.add(key, codeBlock)->install(vm);
+    case PropertyCondition::Equivalence: {
+        AdaptiveInferredPropertyValueWatchpoint* watchpoint = nullptr;
+        {
+            ConcurrentJSLocker locker(codeBlock->m_lock);
+            watchpoint = common.adaptiveInferredPropertyValueWatchpoints.add(key, codeBlock);
+        }
+        watchpoint->install(vm);
         break;
-    default:
-        common.adaptiveStructureWatchpoints.add(key, codeBlock)->install(vm);
+    }
+    default: {
+        AdaptiveStructureWatchpoint* watchpoint = nullptr;
+        {
+            ConcurrentJSLocker locker(codeBlock->m_lock);
+            watchpoint = common.adaptiveStructureWatchpoints.add(key, codeBlock);
+        }
+        watchpoint->install(vm);
         break;
     }
+    }
 }
 
 DesiredWatchpoints::DesiredWatchpoints() { }
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
index f89e789..9e13b62 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
+++ b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
@@ -45,7 +45,12 @@
 struct SetPointerAdaptor {
     static void add(CodeBlock* codeBlock, T set, CommonData& common)
     {
-        return set->add(common.watchpoints.add(codeBlock));
+        CodeBlockJettisoningWatchpoint* watchpoint = nullptr;
+        {
+            ConcurrentJSLocker locker(codeBlock->m_lock);
+            watchpoint = common.watchpoints.add(codeBlock);
+        }
+        return set->add(WTFMove(watchpoint));
     }
     static bool hasBeenInvalidated(T set)
     {
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredWeakReferences.cpp b/Source/JavaScriptCore/dfg/DFGDesiredWeakReferences.cpp
index 68ff2a6..fc27745 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredWeakReferences.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDesiredWeakReferences.cpp
@@ -69,6 +69,7 @@
 {
     for (JSCell* target : m_references) {
         if (Structure* structure = jsDynamicCast<Structure*>(vm, target)) {
+            ConcurrentJSLocker locker(m_codeBlock->m_lock);
             common->weakStructureReferences.append(
                 WriteBarrier<Structure>(vm, m_codeBlock, structure));
         } else {
@@ -78,6 +79,7 @@
             // having a weak pointer to itself will cause it to get collected.
             RELEASE_ASSERT(!jsDynamicCast<CodeBlock*>(vm, target));
 
+            ConcurrentJSLocker locker(m_codeBlock->m_lock);
             common->weakReferences.append(
                 WriteBarrier<JSCell>(vm, m_codeBlock, target));
         }