Title: [261538] trunk/Source
Revision
261538
Author
[email protected]
Date
2020-05-11 19:10:05 -0700 (Mon, 11 May 2020)

Log Message

Introduce WTF::Config and put Signal.cpp's init-once globals in it.
https://bugs.webkit.org/show_bug.cgi?id=211729
<rdar://problem/62938878>

Reviewed by Keith Miller and Saam Barati.

Source/_javascript_Core:

1. Initialize VMTraps' signals early now that we'll be freezing signals at the end
   of the first VM initialization.

2. Move the !initializeThreadingHasBeenCalled RELEASE_ASSERT in initializeThreading()
   to the bottom of the function.  This way, we'll also catch bugs which may cause
   us to jump into the middle of the function.

   Added a compilerFence there to ensure that the RELEASE_ASSERT is only executed
   after all initialization is done.  This guarantees that it will only be executed
   at the end.

3. Call WTF::Config::permanentlyFreeze() from JSC::Config::permanentlyFreeze()
   for obvious reasons: freezing one should freeze the other.

* runtime/InitializeThreading.cpp:
(JSC::initializeThreading):
* runtime/JSCConfig.cpp:
(JSC::Config::permanentlyFreeze):
* runtime/VMTraps.cpp:
(JSC::VMTraps::initializeSignals):
* runtime/VMTraps.h:

Source/WTF:

1. Added WTF::Config for storing globals that effectively serve as constants i.e
   we'll be initializing them before or during the first VM instantiation, but
   should not have any reason to modify them later.  The WTF::Config will be frozen
   (along with JSC::Config) at the end of instantiating the first VM instance.

2. Added a Config::AssertNotFrozenScope RAII object to ensure that initialization
   operations that should only be called at initialization time, will not be
   called once the Config has been frozen.

3. Moved most of Signal.cpp's globals into WTF::Config.  The only globals (or
   statics) not moved are ones that cannot be moved because they require a
   non-trivial default constructor (once_flag), or need to be modifiable
   at runtime (e.g. Lock).

   Instead of freezing the once_flag, we sanity check the call_once block with
   the Config::AssertNotFrozenScope.

4. SignalHandler records are now allocated from arrays of SignalHandlerMemory in
   the WTF::Config.  The number of signal handlers we will ever install is always
   finite.  Hence, there's no reason to use a dynamic data structure like the
   LocklessBag to hold it.

   We introduce a SignalHandlers struct to manage these arrays (and all the other
   Signal.cpp globals that we want to move to the WTF::Config).  Amongst other
   things, SignalHandlers provides the abstractions for:

   a. allocating memory for the SignalHandler instances.  See SignalHandlers::alloc().
   b. iterating SignalHandler instances. See SignalHandlers::forEachHandler().

   To maintain the synchronization properties of the LocklessBag,
   SignalHandlers::alloc() uses a mutex.  In practice, this mutex will never be
   contended on because all signal handlers are now installed at initialization
   time before any concurrency comes into play.

5. We now initialize activeThreads() eagerly via initializeThreading.  In
   production configurations, this does not matter because signal handler
   installations will always trigger its initialization.  However, in debugging
   configurations, we may end up disabling the use of all signal handlers.  As a
   result, we need to do this eager initialization to ensure that it is done
   before we freeze the WTF::Config.

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/Threading.cpp:
(WTF::initializeThreading):
* wtf/WTFConfig.cpp: Added.
(WTF::Config::disableFreezingForTesting):
(WTF::Config::permanentlyFreeze):
* wtf/WTFConfig.h: Added.
(WTF::Config::configureForTesting):
(WTF::Config::AssertNotFrozenScope::~AssertNotFrozenScope):
* wtf/threads/Signals.cpp:
(WTF::SignalHandlers::alloc):
(WTF::SignalHandlers::forEachHandler const):
(WTF::startMachExceptionHandlerThread):
(WTF::handleSignalsWithMach):
(WTF::setExceptionPorts):
(WTF::activeThreads):
(WTF::installSignalHandler):
(WTF::jscSignalHandler):
(WTF::SignalHandlers::initialize):
* wtf/threads/Signals.h:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (261537 => 261538)


--- trunk/Source/_javascript_Core/ChangeLog	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-05-12 02:10:05 UTC (rev 261538)
@@ -1,3 +1,33 @@
+2020-05-11  Mark Lam  <[email protected]>
+
+        Introduce WTF::Config and put Signal.cpp's init-once globals in it.
+        https://bugs.webkit.org/show_bug.cgi?id=211729
+        <rdar://problem/62938878>
+
+        Reviewed by Keith Miller and Saam Barati.
+
+        1. Initialize VMTraps' signals early now that we'll be freezing signals at the end
+           of the first VM initialization.
+
+        2. Move the !initializeThreadingHasBeenCalled RELEASE_ASSERT in initializeThreading()
+           to the bottom of the function.  This way, we'll also catch bugs which may cause
+           us to jump into the middle of the function.
+
+           Added a compilerFence there to ensure that the RELEASE_ASSERT is only executed
+           after all initialization is done.  This guarantees that it will only be executed
+           at the end.
+
+        3. Call WTF::Config::permanentlyFreeze() from JSC::Config::permanentlyFreeze()
+           for obvious reasons: freezing one should freeze the other.
+
+        * runtime/InitializeThreading.cpp:
+        (JSC::initializeThreading):
+        * runtime/JSCConfig.cpp:
+        (JSC::Config::permanentlyFreeze):
+        * runtime/VMTraps.cpp:
+        (JSC::VMTraps::initializeSignals):
+        * runtime/VMTraps.h:
+
 2020-05-11  Keith Miller  <[email protected]>
 
         Remove unused BytecodeKills.h

Modified: trunk/Source/_javascript_Core/runtime/InitializeThreading.cpp (261537 => 261538)


--- trunk/Source/_javascript_Core/runtime/InitializeThreading.cpp	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/_javascript_Core/runtime/InitializeThreading.cpp	2020-05-12 02:10:05 UTC (rev 261538)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -44,6 +44,7 @@
 #include "SigillCrashAnalyzer.h"
 #include "StructureIDTable.h"
 #include "SuperSampler.h"
+#include "VMTraps.h"
 #include "WasmCalleeRegistry.h"
 #include "WasmCapabilities.h"
 #include "WasmThunks.h"
@@ -64,9 +65,6 @@
     static std::once_flag initializeThreadingOnceFlag;
 
     std::call_once(initializeThreadingOnceFlag, []{
-        RELEASE_ASSERT(!g_jscConfig.initializeThreadingHasBeenCalled);
-        g_jscConfig.initializeThreadingHasBeenCalled = true;
-
         WTF::initializeThreading();
         Options::initialize();
 
@@ -105,6 +103,11 @@
         // JSLock::lock() can call registerThreadForMachExceptionHandling() which crashes if this has not been called first.
         WTF::startMachExceptionHandlerThread();
 #endif
+        VMTraps::initializeSignals();
+
+        WTF::compilerFence();
+        RELEASE_ASSERT(!g_jscConfig.initializeThreadingHasBeenCalled);
+        g_jscConfig.initializeThreadingHasBeenCalled = true;
     });
 }
 

Modified: trunk/Source/_javascript_Core/runtime/JSCConfig.cpp (261537 => 261538)


--- trunk/Source/_javascript_Core/runtime/JSCConfig.cpp	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/_javascript_Core/runtime/JSCConfig.cpp	2020-05-12 02:10:05 UTC (rev 261538)
@@ -29,6 +29,7 @@
 #include <wtf/Lock.h>
 #include <wtf/ResourceUsage.h>
 #include <wtf/StdLibExtras.h>
+#include <wtf/WTFConfig.h>
 
 #if OS(DARWIN)
 #include <mach/mach.h>
@@ -54,6 +55,8 @@
     
 void Config::permanentlyFreeze()
 {
+    WTF::Config::permanentlyFreeze();
+
     static Lock configLock;
     auto locker = holdLock(configLock);
 

Modified: trunk/Source/_javascript_Core/runtime/VMTraps.cpp (261537 => 261538)


--- trunk/Source/_javascript_Core/runtime/VMTraps.cpp	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/_javascript_Core/runtime/VMTraps.cpp	2020-05-12 02:10:05 UTC (rev 261538)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -198,6 +198,9 @@
     SignalSender(const AbstractLocker& locker, VM& vm)
         : Base(locker, vm.traps().m_lock, vm.traps().m_condition.copyRef())
         , m_vm(vm)
+    { }
+
+    static void initializeSignals()
     {
         static std::once_flag once;
         std::call_once(once, [] {
@@ -294,6 +297,14 @@
 
 #endif // ENABLE(SIGNAL_BASED_VM_TRAPS)
 
+void VMTraps::initializeSignals()
+{
+#if ENABLE(SIGNAL_BASED_VM_TRAPS)
+    if (!Options::usePollingTraps())
+        SignalSender::initializeSignals();
+#endif
+}
+
 void VMTraps::willDestroyVM()
 {
     m_isShuttingDown = true;

Modified: trunk/Source/_javascript_Core/runtime/VMTraps.h (261537 => 261538)


--- trunk/Source/_javascript_Core/runtime/VMTraps.h	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/_javascript_Core/runtime/VMTraps.h	2020-05-12 02:10:05 UTC (rev 261538)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -98,6 +98,8 @@
     ~VMTraps();
     VMTraps();
 
+    static void initializeSignals();
+
     void willDestroyVM();
 
     bool needTrapHandling() { return m_needTrapHandling; }

Modified: trunk/Source/WTF/ChangeLog (261537 => 261538)


--- trunk/Source/WTF/ChangeLog	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/WTF/ChangeLog	2020-05-12 02:10:05 UTC (rev 261538)
@@ -1,3 +1,74 @@
+2020-05-11  Mark Lam  <[email protected]>
+
+        Introduce WTF::Config and put Signal.cpp's init-once globals in it.
+        https://bugs.webkit.org/show_bug.cgi?id=211729
+        <rdar://problem/62938878>
+
+        Reviewed by Keith Miller and Saam Barati.
+
+        1. Added WTF::Config for storing globals that effectively serve as constants i.e
+           we'll be initializing them before or during the first VM instantiation, but
+           should not have any reason to modify them later.  The WTF::Config will be frozen
+           (along with JSC::Config) at the end of instantiating the first VM instance.
+
+        2. Added a Config::AssertNotFrozenScope RAII object to ensure that initialization
+           operations that should only be called at initialization time, will not be
+           called once the Config has been frozen.
+
+        3. Moved most of Signal.cpp's globals into WTF::Config.  The only globals (or
+           statics) not moved are ones that cannot be moved because they require a
+           non-trivial default constructor (once_flag), or need to be modifiable
+           at runtime (e.g. Lock).
+
+           Instead of freezing the once_flag, we sanity check the call_once block with
+           the Config::AssertNotFrozenScope.
+
+        4. SignalHandler records are now allocated from arrays of SignalHandlerMemory in
+           the WTF::Config.  The number of signal handlers we will ever install is always
+           finite.  Hence, there's no reason to use a dynamic data structure like the
+           LocklessBag to hold it.
+
+           We introduce a SignalHandlers struct to manage these arrays (and all the other
+           Signal.cpp globals that we want to move to the WTF::Config).  Amongst other
+           things, SignalHandlers provides the abstractions for:
+
+           a. allocating memory for the SignalHandler instances.  See SignalHandlers::alloc().
+           b. iterating SignalHandler instances. See SignalHandlers::forEachHandler().
+
+           To maintain the synchronization properties of the LocklessBag,
+           SignalHandlers::alloc() uses a mutex.  In practice, this mutex will never be
+           contended on because all signal handlers are now installed at initialization
+           time before any concurrency comes into play.
+
+        5. We now initialize activeThreads() eagerly via initializeThreading.  In
+           production configurations, this does not matter because signal handler
+           installations will always trigger its initialization.  However, in debugging
+           configurations, we may end up disabling the use of all signal handlers.  As a
+           result, we need to do this eager initialization to ensure that it is done
+           before we freeze the WTF::Config.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/Threading.cpp:
+        (WTF::initializeThreading):
+        * wtf/WTFConfig.cpp: Added.
+        (WTF::Config::disableFreezingForTesting):
+        (WTF::Config::permanentlyFreeze):
+        * wtf/WTFConfig.h: Added.
+        (WTF::Config::configureForTesting):
+        (WTF::Config::AssertNotFrozenScope::~AssertNotFrozenScope):
+        * wtf/threads/Signals.cpp:
+        (WTF::SignalHandlers::alloc):
+        (WTF::SignalHandlers::forEachHandler const):
+        (WTF::startMachExceptionHandlerThread):
+        (WTF::handleSignalsWithMach):
+        (WTF::setExceptionPorts):
+        (WTF::activeThreads):
+        (WTF::installSignalHandler):
+        (WTF::jscSignalHandler):
+        (WTF::SignalHandlers::initialize):
+        * wtf/threads/Signals.h:
+
 2020-05-11  David Kilzer  <[email protected]>
 
         [WTF] CStringBuffer::createUninitialized() should use Checked<size_t>

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (261537 => 261538)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2020-05-12 02:10:05 UTC (rev 261538)
@@ -182,6 +182,7 @@
 		E4A0AD391A96245500536DF6 /* WorkQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A0AD371A96245500536DF6 /* WorkQueue.cpp */; };
 		E4A0AD3D1A96253C00536DF6 /* WorkQueueCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A0AD3C1A96253C00536DF6 /* WorkQueueCocoa.cpp */; };
 		EB61EDC72409CCC1001EFE36 /* SystemTracingCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EB61EDC62409CCC0001EFE36 /* SystemTracingCocoa.cpp */; };
+		FE032AD22463E43B0012D7C7 /* WTFConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE032AD02463E43B0012D7C7 /* WTFConfig.cpp */; };
 		FE05FAFF1FE5007500093230 /* WTFAssertions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE05FAFE1FE5007500093230 /* WTFAssertions.cpp */; };
 		FE1E2C3B2240C06600F6B729 /* PtrTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1E2C392240C05400F6B729 /* PtrTag.cpp */; };
 		FE1E2C42224187C600F6B729 /* PlatformRegisters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1E2C41224187C600F6B729 /* PlatformRegisters.cpp */; };
@@ -738,6 +739,8 @@
 		EBFF67FC240D7D660078FF1B /* OSVariantSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSVariantSPI.h; sourceTree = "<group>"; };
 		EF7D6CD59D8642A8A0DA86AD /* StackTrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackTrace.h; sourceTree = "<group>"; };
 		F72BBDB107FA424886178B9E /* SymbolImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolImpl.cpp; sourceTree = "<group>"; };
+		FE032AD02463E43B0012D7C7 /* WTFConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WTFConfig.cpp; sourceTree = "<group>"; };
+		FE032AD12463E43B0012D7C7 /* WTFConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WTFConfig.h; sourceTree = "<group>"; };
 		FE05FAE61FDB214300093230 /* DumbPtrTraits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumbPtrTraits.h; sourceTree = "<group>"; };
 		FE05FAFE1FE5007500093230 /* WTFAssertions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WTFAssertions.cpp; sourceTree = "<group>"; };
 		FE1D6D87237401CD007A5C26 /* StackCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackCheck.h; sourceTree = "<group>"; };
@@ -1304,6 +1307,8 @@
 				E4A0AD371A96245500536DF6 /* WorkQueue.cpp */,
 				E4A0AD381A96245500536DF6 /* WorkQueue.h */,
 				FE05FAFE1FE5007500093230 /* WTFAssertions.cpp */,
+				FE032AD02463E43B0012D7C7 /* WTFConfig.cpp */,
+				FE032AD12463E43B0012D7C7 /* WTFConfig.h */,
 				A36E16F7216FF828008DD87E /* WTFSemaphore.h */,
 			);
 			path = wtf;
@@ -1761,6 +1766,7 @@
 				A8A47469151A825B004123FF /* UTF8Conversion.cpp in Sources */,
 				7AFEC6B11EB22B5900DADE36 /* UUID.cpp in Sources */,
 				E3149A39228BB43500BFA6C7 /* Vector.cpp in Sources */,
+				FE032AD22463E43B0012D7C7 /* WTFConfig.cpp in Sources */,
 				0F66B2921DC97BAB004A1D3F /* WallTime.cpp in Sources */,
 				1FA47C8A152502DA00568D1B /* WebCoreThread.cpp in Sources */,
 				0FE4479C1B7AAA03009498EB /* WordLock.cpp in Sources */,

Modified: trunk/Source/WTF/wtf/CMakeLists.txt (261537 => 261538)


--- trunk/Source/WTF/wtf/CMakeLists.txt	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/WTF/wtf/CMakeLists.txt	2020-05-12 02:10:05 UTC (rev 261538)
@@ -284,6 +284,7 @@
     Vector.h
     VectorHash.h
     VectorTraits.h
+    WTFConfig.h
     WTFSemaphore.h
     WallTime.h
     WeakHashSet.h
@@ -445,6 +446,7 @@
     UniqueArray.cpp
     Vector.cpp
     WTFAssertions.cpp
+    WTFConfig.cpp
     WallTime.cpp
     WordLock.cpp
     WorkQueue.cpp

Modified: trunk/Source/WTF/wtf/Threading.cpp (261537 => 261538)


--- trunk/Source/WTF/wtf/Threading.cpp	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/WTF/wtf/Threading.cpp	2020-05-12 02:10:05 UTC (rev 261538)
@@ -36,8 +36,10 @@
 #include <wtf/ThreadGroup.h>
 #include <wtf/ThreadMessage.h>
 #include <wtf/ThreadingPrimitives.h>
+#include <wtf/WTFConfig.h>
 #include <wtf/text/AtomStringTable.h>
 #include <wtf/text/StringView.h>
+#include <wtf/threads/Signals.h>
 
 #if HAVE(QOS_CLASSES)
 #include <bmalloc/bmalloc.h>
@@ -360,6 +362,7 @@
 {
     static std::once_flag onceKey;
     std::call_once(onceKey, [] {
+        Config::AssertNotFrozenScope assertScope;
         initializeRandomNumberGenerator();
 #if !HAVE(FAST_TLS) && !OS(WINDOWS)
         Thread::initializeTLSKey();
@@ -366,6 +369,9 @@
 #endif
         initializeDates();
         Thread::initializePlatformThreading();
+#if USE(PTHREADS) && HAVE(MACHINE_CONTEXT)
+        SignalHandlers::initialize();
+#endif
     });
 }
 

Added: trunk/Source/WTF/wtf/WTFConfig.cpp (0 => 261538)


--- trunk/Source/WTF/wtf/WTFConfig.cpp	                        (rev 0)
+++ trunk/Source/WTF/wtf/WTFConfig.cpp	2020-05-12 02:10:05 UTC (rev 261538)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <wtf/WTFConfig.h>
+
+#include <wtf/Lock.h>
+#include <wtf/ResourceUsage.h>
+#include <wtf/StdLibExtras.h>
+
+#if OS(DARWIN)
+#include <mach/mach.h>
+#elif OS(LINUX)
+#include <sys/mman.h>
+#endif
+
+namespace WTF {
+
+alignas(ConfigSizeToProtect) WTF_EXPORT_PRIVATE Config g_wtfConfig;
+
+void Config::permanentlyFreeze()
+{
+    static Lock configLock;
+    auto locker = holdLock(configLock);
+
+    RELEASE_ASSERT(roundUpToMultipleOf(pageSize(), ConfigSizeToProtect) == ConfigSizeToProtect);
+
+    if (!g_wtfConfig.isPermanentlyFrozen)
+        g_wtfConfig.isPermanentlyFrozen = true;
+
+    int result = 0;
+#if OS(DARWIN)
+    enum {
+        AllowPermissionChangesAfterThis = false,
+        DisallowPermissionChangesAfterThis = true
+    };
+
+    // There's no going back now!
+    result = vm_protect(mach_task_self(), reinterpret_cast<vm_address_t>(&g_wtfConfig), ConfigSizeToProtect, DisallowPermissionChangesAfterThis, VM_PROT_READ);
+#elif OS(LINUX)
+    result = mprotect(&g_wtfConfig, ConfigSizeToProtect, PROT_READ);
+#elif OS(WINDOWS)
+    // FIXME: Implement equivalent, maybe with VirtualProtect.
+    // Also need to fix WebKitTestRunner.
+#endif
+    RELEASE_ASSERT(!result);
+    RELEASE_ASSERT(g_wtfConfig.isPermanentlyFrozen);
+}
+
+} // namespace WTF

Added: trunk/Source/WTF/wtf/WTFConfig.h (0 => 261538)


--- trunk/Source/WTF/wtf/WTFConfig.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/WTFConfig.h	2020-05-12 02:10:05 UTC (rev 261538)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Assertions.h>
+#include <wtf/Atomics.h>
+#include <wtf/ExportMacros.h>
+#include <wtf/PageBlock.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/threads/Signals.h>
+
+namespace WTF {
+
+constexpr size_t ConfigSizeToProtect = CeilingOnPageSize;
+
+struct Config {
+    WTF_EXPORT_PRIVATE static void permanentlyFreeze();
+
+    struct AssertNotFrozenScope {
+        AssertNotFrozenScope();
+        ~AssertNotFrozenScope();
+    };
+
+    union {
+        struct {
+            // All the fields in this struct should be chosen such that their
+            // initial value is 0 / null / falsy because Config is instantiated
+            // as a global singleton.
+
+            bool isPermanentlyFrozen;
+
+#if USE(PTHREADS) && HAVE(MACHINE_CONTEXT)
+            SignalHandlers signalHandlers;
+#endif
+        };
+        char ensureSize[ConfigSizeToProtect];
+    };
+};
+
+extern "C" alignas(ConfigSizeToProtect) WTF_EXPORT_PRIVATE Config g_wtfConfig;
+
+static_assert(sizeof(Config) == ConfigSizeToProtect);
+
+ALWAYS_INLINE Config::AssertNotFrozenScope::AssertNotFrozenScope()
+{
+    RELEASE_ASSERT(!g_wtfConfig.isPermanentlyFrozen);
+    compilerFence();
+};
+
+ALWAYS_INLINE Config::AssertNotFrozenScope::~AssertNotFrozenScope()
+{
+    compilerFence();
+    RELEASE_ASSERT(!g_wtfConfig.isPermanentlyFrozen);
+};
+
+} // namespace WTF

Modified: trunk/Source/WTF/wtf/threads/Signals.cpp (261537 => 261538)


--- trunk/Source/WTF/wtf/threads/Signals.cpp	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/WTF/wtf/threads/Signals.cpp	2020-05-12 02:10:05 UTC (rev 261538)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -51,15 +51,42 @@
 #include <wtf/ThreadGroup.h>
 #include <wtf/ThreadMessage.h>
 #include <wtf/Threading.h>
+#include <wtf/WTFConfig.h>
 
-
 namespace WTF {
 
-    
-static LazyNeverDestroyed<LocklessBag<SignalHandler>> handlers[static_cast<size_t>(Signal::NumberOfSignals)] = { };
-static std::once_flag initializeOnceFlags[static_cast<size_t>(Signal::NumberOfSignals)];
-static struct sigaction oldActions[static_cast<size_t>(Signal::NumberOfSignals)];
+void SignalHandlers::add(Signal signal, SignalHandler&& handler)
+{
+    Config::AssertNotFrozenScope assertScope;
+    static Lock lock;
+    auto locker = holdLock(lock);
 
+    size_t signalIndex = static_cast<size_t>(signal);
+    size_t nextFree = numberOfHandlers[signalIndex];
+    RELEASE_ASSERT(nextFree < maxNumberOfHandlers);
+    SignalHandlerMemory* memory = &handlers[signalIndex][nextFree];
+    new (memory) SignalHandler(WTFMove(handler));
+
+    // We deliberately do not want to increment the count until after we've
+    // fully initialized the memory. This way, forEachHandler() won't see a
+    // partially initialized handler.
+    storeStoreFence();
+    numberOfHandlers[signalIndex]++;
+    loadLoadFence();
+}
+
+template<typename Func>
+inline void SignalHandlers::forEachHandler(Signal signal, const Func& func) const
+{
+    size_t signalIndex = static_cast<size_t>(signal);
+    size_t handlerIndex = numberOfHandlers[signalIndex];
+    while (handlerIndex--) {
+        auto* memory = const_cast<SignalHandlerMemory*>(&handlers[signalIndex][handlerIndex]);
+        const SignalHandler& handler = *bitwise_cast<SignalHandler*>(memory);
+        func(handler);
+    }
+}
+
 #if HAVE(MACH_EXCEPTIONS)
 // You can read more about mach exceptions here:
 // http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/exception.ps
@@ -66,7 +93,6 @@
 // and the Mach interface Generator (MiG) here:
 // http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/mig.ps
 
-static mach_port_t exceptionPort;
 static constexpr size_t maxMessageSize = 1 * KB;
 
 void startMachExceptionHandlerThread()
@@ -73,13 +99,15 @@
 {
     static std::once_flag once;
     std::call_once(once, [] {
-        kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exceptionPort);
+        Config::AssertNotFrozenScope assertScope;
+        SignalHandlers& handlers = g_wtfConfig.signalHandlers;
+        kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &handlers.exceptionPort);
         RELEASE_ASSERT(kr == KERN_SUCCESS);
-        kr = mach_port_insert_right(mach_task_self(), exceptionPort, exceptionPort, MACH_MSG_TYPE_MAKE_SEND);
+        kr = mach_port_insert_right(mach_task_self(), handlers.exceptionPort, handlers.exceptionPort, MACH_MSG_TYPE_MAKE_SEND);
         RELEASE_ASSERT(kr == KERN_SUCCESS);
 
         dispatch_source_t source = dispatch_source_create(
-            DISPATCH_SOURCE_TYPE_MACH_RECV, exceptionPort, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
+            DISPATCH_SOURCE_TYPE_MACH_RECV, handlers.exceptionPort, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
         RELEASE_ASSERT(source);
 
         dispatch_source_set_event_handler(source, ^{
@@ -86,7 +114,7 @@
             UNUSED_PARAM(source); // Capture a pointer to source in user space to silence the leaks tool.
 
             kern_return_t kr = mach_msg_server_once(
-                mach_exc_server, maxMessageSize, exceptionPort, MACH_MSG_TIMEOUT_NONE);
+                mach_exc_server, maxMessageSize, handlers.exceptionPort, MACH_MSG_TIMEOUT_NONE);
             RELEASE_ASSERT(kr == KERN_SUCCESS);
         });
 
@@ -145,7 +173,8 @@
     thread_state_t outState,
     mach_msg_type_number_t* outStateCount)
 {
-    RELEASE_ASSERT(port == exceptionPort);
+    SignalHandlers& handlers = g_wtfConfig.signalHandlers;
+    RELEASE_ASSERT(port == handlers.exceptionPort);
     // If we wanted to distinguish between SIGBUS and SIGSEGV for EXC_BAD_ACCESS on Darwin we could do:
     // if (exceptionData[0] == KERN_INVALID_ADDRESS)
     //    signal = SIGSEGV;
@@ -178,7 +207,7 @@
     }
 
     bool didHandle = false;
-    handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
+    handlers.forEachHandler(signal, [&] (const SignalHandler& handler) {
         SignalAction handlerResult = handler(signal, info, registers);
         didHandle |= handlerResult == SignalAction::Handled;
     });
@@ -188,21 +217,19 @@
     return KERN_FAILURE;
 }
 
-};
+}; // extern "C"
 
-static bool useMach { false };
 void handleSignalsWithMach()
 {
-    useMach = true;
+    Config::AssertNotFrozenScope assertScope;
+    g_wtfConfig.signalHandlers.useMach = true;
 }
 
-
-exception_mask_t activeExceptions { 0 };
-
 inline void setExceptionPorts(const AbstractLocker& threadGroupLocker, Thread& thread)
 {
     UNUSED_PARAM(threadGroupLocker);
-    kern_return_t result = thread_set_exception_ports(thread.machThread(), activeExceptions, exceptionPort, EXCEPTION_STATE | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
+    SignalHandlers& handlers = g_wtfConfig.signalHandlers;
+    kern_return_t result = thread_set_exception_ports(thread.machThread(), handlers.activeExceptions, handlers.exceptionPort, EXCEPTION_STATE | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
     if (result != KERN_SUCCESS) {
         dataLogLn("thread set port failed due to ", mach_error_string(result));
         CRASH();
@@ -214,6 +241,7 @@
     static std::once_flag initializeKey;
     static ThreadGroup* activeThreadsPtr = nullptr;
     std::call_once(initializeKey, [&] {
+        Config::AssertNotFrozenScope assertScope;
         static NeverDestroyed<std::shared_ptr<ThreadGroup>> activeThreads { ThreadGroup::create() };
         activeThreadsPtr = activeThreads.get().get();
     });
@@ -242,17 +270,22 @@
 
 void installSignalHandler(Signal signal, SignalHandler&& handler)
 {
+    Config::AssertNotFrozenScope assertScope;
+    SignalHandlers& handlers = g_wtfConfig.signalHandlers;
     ASSERT(signal < Signal::Unknown);
 #if HAVE(MACH_EXCEPTIONS)
-    ASSERT(!useMach || signal != Signal::Usr);
+    ASSERT(!handlers.useMach || signal != Signal::Usr);
 
-    if (useMach)
+    if (handlers.useMach)
         startMachExceptionHandlerThread();
 #endif
 
+    static std::once_flag initializeOnceFlags[static_cast<size_t>(Signal::NumberOfSignals)];
     std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
-        handlers[static_cast<size_t>(signal)].construct();
-
+        Config::AssertNotFrozenScope assertScope;
+#if HAVE(MACH_EXCEPTIONS)
+        bool useMach = handlers.useMach;
+#endif
         if (!useMach) {
             struct sigaction action;
             action.sa_sigaction = jscSignalHandler;
@@ -263,20 +296,19 @@
             RELEASE_ASSERT(!result);
             action.sa_flags = SA_SIGINFO;
             auto systemSignals = toSystemSignal(signal);
-            result = sigaction(std::get<0>(systemSignals), &action, &oldActions[offsetForSystemSignal(std::get<0>(systemSignals))]);
+            result = sigaction(std::get<0>(systemSignals), &action, &handlers.oldActions[offsetForSystemSignal(std::get<0>(systemSignals))]);
             if (std::get<1>(systemSignals))
-                result |= sigaction(*std::get<1>(systemSignals), &action, &oldActions[offsetForSystemSignal(*std::get<1>(systemSignals))]);
+                result |= sigaction(*std::get<1>(systemSignals), &action, &handlers.oldActions[offsetForSystemSignal(*std::get<1>(systemSignals))]);
             RELEASE_ASSERT(!result);
         }
-
     });
 
-    handlers[static_cast<size_t>(signal)]->add(WTFMove(handler));
+    handlers.add(signal, WTFMove(handler));
 
 #if HAVE(MACH_EXCEPTIONS)
     auto locker = holdLock(activeThreads().getLock());
-    if (useMach) {
-        activeExceptions |= toMachMask(signal);
+    if (handlers.useMach) {
+        handlers.activeExceptions |= toMachMask(signal);
 
         for (auto& thread : activeThreads().threads(locker))
             setExceptionPorts(locker, thread.get());
@@ -287,6 +319,7 @@
 void jscSignalHandler(int sig, siginfo_t* info, void* ucontext)
 {
     Signal signal = fromSystemSignal(sig);
+    SignalHandlers& handlers = g_wtfConfig.signalHandlers;
 
     auto restoreDefault = [&] {
         struct sigaction defaultAction;
@@ -299,7 +332,7 @@
 
     // This shouldn't happen but we might as well be careful.
     if (signal == Signal::Unknown) {
-        dataLogLn("We somehow got called for an unknown signal ", sig, ", halp.");
+        dataLogLn("We somehow got called for an unknown signal ", sig, ", help.");
         restoreDefault();
         return;
     }
@@ -312,7 +345,7 @@
 
     bool didHandle = false;
     bool restoreDefaultHandler = false;
-    handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
+    handlers.forEachHandler(signal, [&] (const SignalHandler& handler) {
         switch (handler(signal, sigInfo, registers)) {
         case SignalAction::Handled:
             didHandle = true;
@@ -331,7 +364,7 @@
     }
 
     unsigned oldActionIndex = static_cast<size_t>(signal) + (sig == SIGBUS);
-    struct sigaction& oldAction = oldActions[static_cast<size_t>(oldActionIndex)];
+    struct sigaction& oldAction = handlers.oldActions[static_cast<size_t>(oldActionIndex)];
     if (signal == Signal::Usr) {
         if (oldAction.sa_sigaction)
             oldAction.sa_sigaction(sig, info, ucontext);
@@ -349,6 +382,18 @@
     }
 }
 
+void SignalHandlers::initialize()
+{
+#if HAVE(MACH_EXCEPTIONS)
+    // In production configurations, this does not matter because signal handler
+    // installations will always trigger this initialization. However, in debugging
+    // configurations, we may end up disabling the use of all signal handlers but
+    // we still need this to be initialized. Hence, we need to initialize it
+    // eagerly to ensure that it is done before we freeze the WTF::Config.
+    activeThreads();
+#endif
+}
+
 } // namespace WTF
 
 #endif // USE(PTHREADS) && HAVE(MACHINE_CONTEXT)

Modified: trunk/Source/WTF/wtf/threads/Signals.h (261537 => 261538)


--- trunk/Source/WTF/wtf/threads/Signals.h	2020-05-12 01:58:39 UTC (rev 261537)
+++ trunk/Source/WTF/wtf/threads/Signals.h	2020-05-12 02:10:05 UTC (rev 261538)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,9 +30,14 @@
 #include <signal.h>
 #include <tuple>
 #include <wtf/Function.h>
+#include <wtf/Lock.h>
 #include <wtf/Optional.h>
 #include <wtf/PlatformRegisters.h>
 
+#if HAVE(MACH_EXCEPTIONS)
+#include <mach/exception_types.h>
+#endif
+
 namespace WTF {
 
 // Note that SIGUSR1 is used in Pthread-based ports except for Darwin to suspend and resume threads.
@@ -82,11 +87,36 @@
 };
 
 using SignalHandler = Function<SignalAction(Signal, SigInfo&, PlatformRegisters&)>;
+using SignalHandlerMemory = std::aligned_storage<sizeof(SignalHandler), std::alignment_of<SignalHandler>::value>::type;
 
-// Call this method whenever you want to install a signal handler. It's ok to call this function lazily.
+struct SignalHandlers {
+    static void initialize();
+
+    void add(Signal, SignalHandler&&);
+    template<typename Func>
+    void forEachHandler(Signal, const Func&) const;
+
+    static constexpr size_t numberOfSignals = static_cast<size_t>(Signal::NumberOfSignals);
+    static constexpr size_t maxNumberOfHandlers = 2;
+
+    static_assert(numberOfSignals < std::numeric_limits<uint8_t>::max());
+
+#if HAVE(MACH_EXCEPTIONS)
+    mach_port_t exceptionPort;
+    exception_mask_t activeExceptions;
+    bool useMach;
+#endif
+    uint8_t numberOfHandlers[numberOfSignals];
+    SignalHandlerMemory handlers[numberOfSignals][maxNumberOfHandlers];
+    struct sigaction oldActions[numberOfSignals];
+};
+
+// Call this method whenever you want to install a signal handler. This function needs to be called
+// before g_wtfConfig is frozen. After the g_wtfConfig is frozen, no additional signal handlers may
+// be installed. Any attempt to do so will trigger a crash.
 // Note: Your signal handler will be called every time the handler for the desired signal is called.
 // Thus it is your responsibility to discern if the signal fired was yours.
-// This function is currently a one way street i.e. once installed, a signal handler cannot be uninstalled.
+// This function is a one way street i.e. once installed, a signal handler cannot be uninstalled.
 WTF_EXPORT_PRIVATE void installSignalHandler(Signal, SignalHandler&&);
 
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to