Put g_gigacageBasePtr into its own page and make it read-only
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Sep 2017 05:05:27 +0000 (05:05 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Sep 2017 05:05:27 +0000 (05:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174972

Reviewed by Michael Saboff.

Source/bmalloc:

This puts the gigacage base pointers into their own page and makes that page read-only.

* bmalloc/Gigacage.cpp:
(Gigacage::ensureGigacage):
(Gigacage::disablePrimitiveGigacage):
(Gigacage::addPrimitiveDisableCallback):
* bmalloc/Gigacage.h:
(Gigacage::basePtr):
(Gigacage::basePtrs):

Source/JavaScriptCore:

C++ code doesn't have to know about this change. That includes C++ code that generates JIT code.

But the offline assembler now needs to know about how to load from offsets of global variables.
This turned out to be easy to support by extending the existing expression support.

* llint/LowLevelInterpreter64.asm:
* offlineasm/ast.rb:
* offlineasm/parser.rb:
* offlineasm/transform.rb:
* offlineasm/x86.rb:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@222549 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/offlineasm/ast.rb
Source/JavaScriptCore/offlineasm/parser.rb
Source/JavaScriptCore/offlineasm/transform.rb
Source/JavaScriptCore/offlineasm/x86.rb
Source/bmalloc/ChangeLog
Source/bmalloc/bmalloc/Gigacage.cpp
Source/bmalloc/bmalloc/Gigacage.h

index 651c9cc..7ecb640 100644 (file)
@@ -1,3 +1,21 @@
+2017-09-26  Filip Pizlo  <fpizlo@apple.com>
+
+        Put g_gigacageBasePtr into its own page and make it read-only
+        https://bugs.webkit.org/show_bug.cgi?id=174972
+
+        Reviewed by Michael Saboff.
+        
+        C++ code doesn't have to know about this change. That includes C++ code that generates JIT code.
+        
+        But the offline assembler now needs to know about how to load from offsets of global variables.
+        This turned out to be easy to support by extending the existing expression support.
+
+        * llint/LowLevelInterpreter64.asm:
+        * offlineasm/ast.rb:
+        * offlineasm/parser.rb:
+        * offlineasm/transform.rb:
+        * offlineasm/x86.rb:
+
 2017-09-26  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r222518.
index e37a0f6..5d62987 100644 (file)
@@ -1209,7 +1209,7 @@ _llint_op_is_object:
 
 macro loadPropertyAtVariableOffset(propertyOffsetAsInt, objectAndStorage, value)
     bilt propertyOffsetAsInt, firstOutOfLineOffset, .isInline
-    loadCaged(_g_jsValueGigacageBasePtr, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[objectAndStorage], objectAndStorage, value)
+    loadCaged(_g_gigacageBasePtrs + Gigacage::BasePtrs::jsValue, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[objectAndStorage], objectAndStorage, value)
     negi propertyOffsetAsInt
     sxi2q propertyOffsetAsInt, propertyOffsetAsInt
     jmp .ready
@@ -1222,7 +1222,7 @@ end
 
 macro storePropertyAtVariableOffset(propertyOffsetAsInt, objectAndStorage, value, scratch)
     bilt propertyOffsetAsInt, firstOutOfLineOffset, .isInline
-    loadCaged(_g_jsValueGigacageBasePtr, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[objectAndStorage], objectAndStorage, scratch)
+    loadCaged(_g_gigacageBasePtrs + Gigacage::BasePtrs::jsValue, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[objectAndStorage], objectAndStorage, scratch)
     negi propertyOffsetAsInt
     sxi2q propertyOffsetAsInt, propertyOffsetAsInt
     jmp .ready
@@ -1298,7 +1298,7 @@ _llint_op_get_array_length:
     btiz t2, IsArray, .opGetArrayLengthSlow
     btiz t2, IndexingShapeMask, .opGetArrayLengthSlow
     loadisFromInstruction(1, t1)
-    loadCaged(_g_jsValueGigacageBasePtr, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[t3], t0, t2)
+    loadCaged(_g_gigacageBasePtrs + Gigacage::BasePtrs::jsValue, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[t3], t0, t2)
     loadi -sizeof IndexingHeader + IndexingHeader::u.lengths.publicLength[t0], t0
     bilt t0, 0, .opGetArrayLengthSlow
     orq tagTypeNumber, t0
@@ -1481,7 +1481,7 @@ _llint_op_get_by_val:
     loadisFromInstruction(3, t3)
     loadConstantOrVariableInt32(t3, t1, .opGetByValSlow)
     sxi2q t1, t1
-    loadCaged(_g_jsValueGigacageBasePtr, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[t0], t3, t5)
+    loadCaged(_g_gigacageBasePtrs + Gigacage::BasePtrs::jsValue, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[t0], t3, t5)
     andi IndexingShapeMask, t2
     bieq t2, Int32Shape, .opGetByValIsContiguous
     bineq t2, ContiguousShape, .opGetByValNotContiguous
@@ -1528,7 +1528,7 @@ _llint_op_get_by_val:
     bia t2, LastArrayType - FirstArrayType, .opGetByValSlow
     
     # Sweet, now we know that we have a typed array. Do some basic things now.
-    loadCaged(_g_primitiveGigacageBasePtr, constexpr PRIMITIVE_GIGACAGE_MASK, JSArrayBufferView::m_vector[t0], t3, t5)
+    loadCaged(_g_gigacageBasePtrs + Gigacage::BasePtrs::primitive, constexpr PRIMITIVE_GIGACAGE_MASK, JSArrayBufferView::m_vector[t0], t3, t5)
     biaeq t1, JSArrayBufferView::m_length[t0], .opGetByValSlow
     
     # Now bisect through the various types. Note that we can treat Uint8ArrayType and
@@ -1619,7 +1619,7 @@ macro putByVal(slowPath)
     loadisFromInstruction(2, t0)
     loadConstantOrVariableInt32(t0, t3, .opPutByValSlow)
     sxi2q t3, t3
-    loadCaged(_g_jsValueGigacageBasePtr, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[t1], t0, t5)
+    loadCaged(_g_gigacageBasePtrs + Gigacage::BasePtrs::jsValue, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[t1], t0, t5)
     andi IndexingShapeMask, t2
     bineq t2, Int32Shape, .opPutByValNotInt32
     contiguousPutByVal(
index de995b9..0d4aa66 100644 (file)
@@ -1110,10 +1110,18 @@ end
 
 class LabelReference < Node
     attr_reader :label
+    attr_accessor :offset
     
     def initialize(codeOrigin, label)
         super(codeOrigin)
         @label = label
+        @offset = 0
+    end
+    
+    def plusOffset(additionalOffset)
+        result = LabelReference.new(codeOrigin, label)
+        result.offset = @offset + additionalOffset
+        result
     end
     
     def children
index 9f4eb1e..3869e6c 100644 (file)
@@ -516,7 +516,7 @@ class Parser
     end
     
     def couldBeExpression
-        @tokens[@idx] == "-" or @tokens[@idx] == "~" or @tokens[@idx] == "sizeof" or @tokens[@idx] == "constexpr" or isInteger(@tokens[@idx]) or isString(@tokens[@idx]) or isVariable(@tokens[@idx]) or @tokens[@idx] == "("
+        @tokens[@idx] == "-" or @tokens[@idx] == "~" or @tokens[@idx] == "sizeof" or @tokens[@idx] == "constexpr" or isInteger(@tokens[@idx]) or isString(@tokens[@idx]) or isVariable(@tokens[@idx]) or isLabel(@tokens[@idx]) or @tokens[@idx] == "("
     end
     
     def parseExpressionAdd
@@ -574,10 +574,6 @@ class Parser
             end
         elsif @tokens[@idx] == "["
             parseAddress(Immediate.new(@tokens[@idx].codeOrigin, 0))
-        elsif isLabel @tokens[@idx]
-            result = LabelReference.new(@tokens[@idx].codeOrigin, Label.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
-            @idx += 1
-            result
         elsif isLocalLabel @tokens[@idx]
             result = LocalLabelReference.new(@tokens[@idx].codeOrigin, LocalLabel.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
             @idx += 1
index e0c4a68..2a08255 100644 (file)
@@ -312,6 +312,10 @@ class AddImmediates
     def fold
         @left = @left.fold
         @right = @right.fold
+        
+        return right.plusOffset(@left.value) if @left.is_a? Immediate and @right.is_a? LabelReference
+        return left.plusOffset(@right.value) if @left.is_a? LabelReference and @right.is_a? Immediate
+        
         return self unless @left.is_a? Immediate
         return self unless @right.is_a? Immediate
         Immediate.new(codeOrigin, @left.value + @right.value)
@@ -322,6 +326,9 @@ class SubImmediates
     def fold
         @left = @left.fold
         @right = @right.fold
+        
+        return left.plusOffset(-@right.value) if @left.is_a? LabelReference and @right.is_a? Immediate
+        
         return self unless @left.is_a? Immediate
         return self unless @right.is_a? Immediate
         Immediate.new(codeOrigin, @left.value - @right.value)
index 4ee1f15..a35a940 100644 (file)
@@ -466,7 +466,7 @@ class LabelReference
         # FIXME: Implement this on platforms that aren't Mach-O.
         # https://bugs.webkit.org/show_bug.cgi?id=175104
         $asm.puts "movq #{asmLabel}@GOTPCREL(%rip), #{dst.x86Operand(:ptr)}"
-        "(#{dst.x86Operand(kind)})"
+        "#{offset}(#{dst.x86Operand(kind)})"
     end
 end
 
index 14ade0f..8c255ac 100644 (file)
@@ -1,3 +1,20 @@
+2017-09-26  Filip Pizlo  <fpizlo@apple.com>
+
+        Put g_gigacageBasePtr into its own page and make it read-only
+        https://bugs.webkit.org/show_bug.cgi?id=174972
+
+        Reviewed by Michael Saboff.
+        
+        This puts the gigacage base pointers into their own page and makes that page read-only.
+
+        * bmalloc/Gigacage.cpp:
+        (Gigacage::ensureGigacage):
+        (Gigacage::disablePrimitiveGigacage):
+        (Gigacage::addPrimitiveDisableCallback):
+        * bmalloc/Gigacage.h:
+        (Gigacage::basePtr):
+        (Gigacage::basePtrs):
+
 2017-09-04  Adrian Perez de Castro  <aperez@igalia.com>
 
         Unreviewed build fix for Clang with libc++
index 4f62f15..463ef16 100644 (file)
 #include <cstdio>
 #include <mutex>
 
-// FIXME: Ask dyld to put this in its own page, and mprotect the page after we ensure the gigacage.
-// https://bugs.webkit.org/show_bug.cgi?id=174972
-void* g_primitiveGigacageBasePtr;
-void* g_jsValueGigacageBasePtr;
-void* g_stringGigacageBasePtr;
+char g_gigacageBasePtrs[GIGACAGE_BASE_PTRS_SIZE] __attribute__((aligned(GIGACAGE_BASE_PTRS_SIZE)));
 
 using namespace bmalloc;
 
 namespace Gigacage {
 
-static bool s_isDisablingPrimitiveGigacageDisabled;
+namespace {
+
+bool s_isDisablingPrimitiveGigacageDisabled;
+
+void protectGigacageBasePtrs()
+{
+    uintptr_t basePtrs = reinterpret_cast<uintptr_t>(g_gigacageBasePtrs);
+    // We might only get page size alignment, but that's also the minimum we need.
+    RELEASE_BASSERT(!(basePtrs & (vmPageSize() - 1)));
+    mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ);
+}
+
+void unprotectGigacageBasePtrs()
+{
+    mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ | PROT_WRITE);
+}
+
+class UnprotectGigacageBasePtrsScope {
+public:
+    UnprotectGigacageBasePtrsScope()
+    {
+        unprotectGigacageBasePtrs();
+    }
+    
+    ~UnprotectGigacageBasePtrsScope()
+    {
+        protectGigacageBasePtrs();
+    }
+};
+
+} // anonymous namespce
 
 struct Callback {
     Callback() { }
@@ -86,6 +112,8 @@ void ensureGigacage()
                     
                     vmDeallocatePhysicalPages(basePtr(kind), totalSize(kind));
                 });
+            
+            protectGigacageBasePtrs();
         });
 #endif // GIGACAGE_ENABLED
 }
@@ -93,7 +121,7 @@ void ensureGigacage()
 void disablePrimitiveGigacage()
 {
     ensureGigacage();
-    if (!g_primitiveGigacageBasePtr) {
+    if (!basePtrs().primitive) {
         // It was never enabled. That means that we never even saved any callbacks. Or, we had already disabled
         // it before, and already called the callbacks.
         return;
@@ -104,13 +132,14 @@ void disablePrimitiveGigacage()
     for (Callback& callback : callbacks.callbacks)
         callback.function(callback.argument);
     callbacks.callbacks.shrink(0);
-    g_primitiveGigacageBasePtr = nullptr;
+    UnprotectGigacageBasePtrsScope unprotectScope;
+    basePtrs().primitive = nullptr;
 }
 
 void addPrimitiveDisableCallback(void (*function)(void*), void* argument)
 {
     ensureGigacage();
-    if (!g_primitiveGigacageBasePtr) {
+    if (!basePtrs().primitive) {
         // It was already disabled or we were never able to enable it.
         function(argument);
         return;
index be4f88d..46ecb94 100644 (file)
 #define GIGACAGE_ENABLED 0
 #endif
 
-extern "C" BEXPORT void* g_primitiveGigacageBasePtr;
-extern "C" BEXPORT void* g_jsValueGigacageBasePtr;
-extern "C" BEXPORT void* g_stringGigacageBasePtr;
+#define GIGACAGE_BASE_PTRS_SIZE 8192
+
+extern "C" BEXPORT char g_gigacageBasePtrs[GIGACAGE_BASE_PTRS_SIZE] __attribute__((aligned(GIGACAGE_BASE_PTRS_SIZE)));
 
 namespace Gigacage {
 
+struct BasePtrs {
+    void* primitive;
+    void* jsValue;
+    void* string;
+};
+
 enum Kind {
     Primitive,
     JSValue,
@@ -97,18 +103,28 @@ BINLINE const char* name(Kind kind)
     return nullptr;
 }
 
-BINLINE void*& basePtr(Kind kind)
+BINLINE void*& basePtr(BasePtrs& basePtrs, Kind kind)
 {
     switch (kind) {
     case Primitive:
-        return g_primitiveGigacageBasePtr;
+        return basePtrs.primitive;
     case JSValue:
-        return g_jsValueGigacageBasePtr;
+        return basePtrs.jsValue;
     case String:
-        return g_stringGigacageBasePtr;
+        return basePtrs.string;
     }
     BCRASH();
-    return g_primitiveGigacageBasePtr;
+    return basePtrs.primitive;
+}
+
+BINLINE BasePtrs& basePtrs()
+{
+    return *reinterpret_cast<BasePtrs*>(g_gigacageBasePtrs);
+}
+
+BINLINE void*& basePtr(Kind kind)
+{
+    return basePtr(basePtrs(), kind);
 }
 
 BINLINE size_t size(Kind kind)