bmalloc should use JSC VM tag for gigacage
[WebKit-https.git] / Source / bmalloc / bmalloc / Gigacage.cpp
index d13f00c..a88623d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "CryptoRandom.h"
 #include "Environment.h"
 #include "PerProcess.h"
+#include "ProcessCheck.h"
 #include "VMAllocate.h"
 #include "Vector.h"
 #include "bmalloc.h"
 #include <cstdio>
 #include <mutex>
 
-#if BCPU(ARM64)
-// FIXME: There is no good reason for ARM64 to be special.
-// https://bugs.webkit.org/show_bug.cgi?id=177605
-#define GIGACAGE_RUNWAY 0
-#else
+// This is exactly 32GB because inside JSC, indexed accesses for arrays, typed arrays, etc,
+// use unsigned 32-bit ints as indices. The items those indices access are 8 bytes or less
+// in size. 2^32 * 8 = 32GB. This means if an access on a caged type happens to go out of
+// bounds, the access is guaranteed to land somewhere else in the cage or inside the runway.
+// If this were less than 32GB, those OOB accesses could reach outside of the cage.
 #define GIGACAGE_RUNWAY (32llu * 1024 * 1024 * 1024)
-#endif
 
-char g_gigacageBasePtrs[GIGACAGE_BASE_PTRS_SIZE] __attribute__((aligned(GIGACAGE_BASE_PTRS_SIZE)));
+// Note: g_gigacageBasePtrs[0] is reserved for storing the wasEnabled flag.
+// The first gigacageBasePtr will start at g_gigacageBasePtrs[sizeof(void*)].
+// This is done so that the wasEnabled flag will also be protected along with the
+// gigacageBasePtrs.
+alignas(GIGACAGE_BASE_PTRS_SIZE) char g_gigacageBasePtrs[GIGACAGE_BASE_PTRS_SIZE];
 
 using namespace bmalloc;
 
 namespace Gigacage {
 
-bool g_wasEnabled;
-
 namespace {
 
 bool s_isDisablingPrimitiveGigacageDisabled;
@@ -94,11 +96,26 @@ struct Callback {
 };
 
 struct PrimitiveDisableCallbacks {
-    PrimitiveDisableCallbacks(std::lock_guard<StaticMutex>&) { }
+    PrimitiveDisableCallbacks(std::lock_guard<Mutex>&) { }
     
     Vector<Callback> callbacks;
 };
 
+#if GIGACAGE_ENABLED
+size_t runwaySize(Kind kind)
+{
+    switch (kind) {
+    case Kind::ReservedForFlagsAndNotABasePtr:
+        RELEASE_BASSERT_NOT_REACHED();
+    case Kind::Primitive:
+        return static_cast<size_t>(GIGACAGE_RUNWAY);
+    case Kind::JSValue:
+        return static_cast<size_t>(0);
+    }
+    return static_cast<size_t>(0);
+}
+#endif
+
 } // anonymous namespace
 
 void ensureGigacage()
@@ -113,7 +130,7 @@ void ensureGigacage()
             
             Kind shuffledKinds[numKinds];
             for (unsigned i = 0; i < numKinds; ++i)
-                shuffledKinds[i] = static_cast<Kind>(i);
+                shuffledKinds[i] = static_cast<Kind>(i + 1); // + 1 to skip Kind::ReservedForFlagsAndNotABasePtr.
             
             // We just go ahead and assume that 64 bits is enough randomness. That's trivially true right
             // now, but would stop being true if we went crazy with gigacages. Based on my math, 21 is the
@@ -140,37 +157,37 @@ void ensureGigacage()
             
             for (Kind kind : shuffledKinds) {
                 totalSize = bump(kind, alignTo(kind, totalSize));
+                totalSize += runwaySize(kind);
                 maxAlignment = std::max(maxAlignment, alignment(kind));
             }
-            totalSize += GIGACAGE_RUNWAY;
-            
+
             // FIXME: Randomize where this goes.
             // https://bugs.webkit.org/show_bug.cgi?id=175245
-            void* base = tryVMAllocate(maxAlignment, totalSize);
+            void* base = tryVMAllocate(maxAlignment, totalSize, VMTag::JSGigacage);
             if (!base) {
                 if (GIGACAGE_ALLOCATION_CAN_FAIL)
                     return;
                 fprintf(stderr, "FATAL: Could not allocate gigacage memory with maxAlignment = %lu, totalSize = %lu.\n", maxAlignment, totalSize);
+                fprintf(stderr, "(Make sure you have not set a virtual memory limit.)\n");
                 BCRASH();
             }
 
-            if (GIGACAGE_RUNWAY > 0) {
-                char* runway = reinterpret_cast<char*>(base) + totalSize - GIGACAGE_RUNWAY;
-                // Make OOB accesses into the runway crash.
-                vmRevokePermissions(runway, GIGACAGE_RUNWAY);
-            }
-
-            vmDeallocatePhysicalPages(base, totalSize);
-            
             size_t nextCage = 0;
             for (Kind kind : shuffledKinds) {
                 nextCage = alignTo(kind, nextCage);
                 basePtr(kind) = reinterpret_cast<char*>(base) + nextCage;
                 nextCage = bump(kind, nextCage);
+                if (runwaySize(kind) > 0) {
+                    char* runway = reinterpret_cast<char*>(base) + nextCage;
+                    // Make OOB accesses into the runway crash.
+                    vmRevokePermissions(runway, runwaySize(kind));
+                    nextCage += runwaySize(kind);
+                }
             }
             
+            vmDeallocatePhysicalPages(base, totalSize);
+            setWasEnabled();
             protectGigacageBasePtrs();
-            g_wasEnabled = true;
         });
 #endif // GIGACAGE_ENABLED
 }
@@ -185,7 +202,7 @@ void disablePrimitiveGigacage()
     }
     
     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
-    std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
+    std::unique_lock<Mutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
     for (Callback& callback : callbacks.callbacks)
         callback.function(callback.argument);
     callbacks.callbacks.shrink(0);
@@ -203,14 +220,14 @@ void addPrimitiveDisableCallback(void (*function)(void*), void* argument)
     }
     
     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
-    std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
+    std::unique_lock<Mutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
     callbacks.callbacks.push(Callback(function, argument));
 }
 
 void removePrimitiveDisableCallback(void (*function)(void*), void* argument)
 {
     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
-    std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
+    std::unique_lock<Mutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
     for (size_t i = 0; i < callbacks.callbacks.size(); ++i) {
         if (callbacks.callbacks[i].function == function
             && callbacks.callbacks[i].argument == argument) {
@@ -223,6 +240,9 @@ void removePrimitiveDisableCallback(void (*function)(void*), void* argument)
 
 static void primitiveGigacageDisabled(void*)
 {
+    if (GIGACAGE_ALLOCATION_CAN_FAIL && !wasEnabled())
+        return;
+
     static bool s_false;
     fprintf(stderr, "FATAL: Primitive gigacage disabled, but we don't want that in this process.\n");
     if (!s_false)
@@ -244,17 +264,17 @@ bool isDisablingPrimitiveGigacageDisabled()
 
 bool shouldBeEnabled()
 {
-    static std::once_flag onceFlag;
     static bool cached = false;
+
+#if GIGACAGE_ENABLED
+    static std::once_flag onceFlag;
     std::call_once(
         onceFlag,
         [] {
-#if BCPU(ARM64)
-            // FIXME: Make WasmBench run with gigacage on iOS and re-enable on ARM64:
-            // https://bugs.webkit.org/show_bug.cgi?id=178557
-            return;
-#endif
-            bool result = GIGACAGE_ENABLED && !PerProcess<Environment>::get()->isDebugHeapEnabled();
+            if (!gigacageEnabledForProcess())
+                return;
+
+            bool result = !PerProcess<Environment>::get()->isDebugHeapEnabled();
             if (!result)
                 return;
             
@@ -268,6 +288,7 @@ bool shouldBeEnabled()
             
             cached = true;
         });
+#endif // GIGACAGE_ENABLED
     
     return cached;
 }