Introducing Integrity audit functions.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Sep 2019 06:02:30 +0000 (06:02 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Sep 2019 06:02:30 +0000 (06:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202085

Reviewed by Saam Barati.

This patch's main goal is to introduce the Integrity audit functions.  They can
be used wherever we want to audit a cell to probabilistically ensure it is not
corrupted.  However, to keep this patch small, we will only introduce the audit
tool here with one example use in SlotVisitor.  We'll follow up later with more
patches to deploy this tool throughout the VM.

1. Introduced Integrity audit functions that can be configured at several
   AuditLevels:
       None - don't do any audits.
       Minimal - do a minimal quick audit (minimize perf impact).
       Full - do a full audit of the many aspects of a cell.
       Random - randomly do a full audit with a probability dictated by
            Options::randomIntegrityAuditRate() between 0.0 (never audit) and
            1.0 (audit at every chance).

   The default AuditLevel for Debug builds is Random.
   The default AuditLevel for Release builds is None.
   The default Options::randomIntegrityAuditRate() is 0.05.

   How full audits work?
   ====================
   The full audit uses the VMInspector::verifyCell() template function to do its
   job.  The reason for keeping this separate is to allow the template function
   to be used later for debug checks that want to take some custom action on
   verification failure instead of crashing with a RELEASE_ASSERT.

   Full audit of a cell pointer includes:
   a. Verify that a cell designated as a LargeAllocation is in the heap's
      set of LargeAllocations.

   b. Verify that a cell not designated as a LargeAllocation is actually in its
      MarkedBlock's bounds.

   c. Verify that the cell's container (LargeAllocation / MarkedBlock) actually
      belongs to the current VM.

   d. Verify that a cell in a MarkedBlock is properly aligned on the block's
      allocation unit size.

   e. If the cell is not an ImmutableButterfly, verify that it is not located in
      the Gigacage.

   f. Verify that the cell's JSType matches its StructureBlob's JSType.

   g. Verify that the cell size as dictated by the cell ClassInfo does not exceed
      the size of the allocation unit size (as expected by the container
      MarkedBlock or LargeAllocation).

      Some cells are dynamically size (see isDynamicallySizedType()).  For these
      cells, we compute their sizes and verify that the size does not exceed the
      allocation unit size.  Their sizes should also be greater or equal to the
      static cell size as dictated by their ClassInfo.

   h. If a cell has a butterfly, verify that the butterfly is in its the JSValue
      Gigacage.

   We can add more verifications later, or make some these more robust, but this
   is a start for now.

   How random audits work?
   ======================
   Random audits are triggered by the m_triggerBits bits in VM::m_integrityRandom.
   m_triggerBits is a 64-bit bitfield.

   If Options::randomIntegrityAuditRate() is 0, m_triggerBits will always be 0,
   and no audits will be done.

   If Options::randomIntegrityAuditRate() is non-zero, m_triggerBits will be
   initialized as follows:

        | 1 reload bit | ... 63 trigger bits ... |

   The reload bit is always set (more details below).
   Each of the 63 trigger bits are randomly set depending if the following is true
   for the bit:

        VM::random() <= Options::randomIntegrityAuditRate() * UINT_MAX

   When Integrity::auditCell() is called, we take the bottom bit as the trigger
   bit for the current cell, and shifts the rest down by 1.

   If m_triggerBits is non-null after the shift, the taken trigger bit will dictate
   whether we do a full audit on the current cell or not.

   Once the reload bit reaches the bottom, we call a reload function to
   re-initialize m_triggerBits.  The reload function also returns a bool
   indicating whether to trigger a full audit of the current cell.

   With this scheme, we only need to call the reload function once every 64 calls
   to Integrity::auditCell(), and can efficiently determine whether to trigger
   the audit the other 63 times with the probability specified in
   Options::randomIntegrityAuditRate().

2. Embedded the C++ class size of JSCells into their ClassInfo.  This is used in
   the full audits to verify cell sizes.

3. Added isDynamicallySizedType() to check if a JSType has a dynamic size allocation
   i.e. the size of instances of this type is not determined by the static C++
   size of its class, but rather, depends on some runtime variable.

4. Made the VMInspector a friend of several classes so that it can access their
   private methods and fields.

5. Moved the inline function JSBigInt::allocationSize() from BigInt.cpp to its
   header file so that we can use it in VMInspector::verifyCellSize().

6. Gave the JSModuleNamespaceObject() its own JSType so that we can identify it
   as a dynamically sized object.

7. Increased the randomness of VM::random() (which is implemented with WeakRandom)
   by re-seeding it with a cryptographically random number each GC.

8. Called Integrity::auditCell() on SlotVisitor::appendJSCellOrAuxiliary()'s cell
   as an example use of auditCell().  More uses will be added in later patches to
   follow.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* heap/Heap.cpp:
(JSC::Heap::runBeginPhase):
* heap/SlotVisitor.cpp:
(JSC::SlotVisitor::appendJSCellOrAuxiliary):
* runtime/ClassInfo.h:
* runtime/DirectArguments.h:
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::allocationSize): Deleted.
* runtime/JSBigInt.h:
(JSC::JSBigInt::allocationSize):
* runtime/JSModuleNamespaceObject.h:
* runtime/JSType.cpp:
(WTF::printInternal):
* runtime/JSType.h:
(JSC::isDynamicallySizedType):
* runtime/Options.cpp:
(JSC::recomputeDependentOptions):
* runtime/OptionsList.h:
* runtime/Structure.h:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
(JSC::VM::random):
(JSC::VM::integrityRandom):
* tools/Integrity.cpp: Added.
(JSC::Integrity::Random::Random):
(JSC::Integrity::Random::reloadAndCheckShouldAuditSlow):
(JSC::Integrity::auditCellFully):
(JSC::Integrity::auditCellMinimallySlow):
* tools/Integrity.h: Added.
(JSC::Integrity::auditCell):
* tools/IntegrityInlines.h: Added.
(JSC::Integrity::Random::shouldAudit):
(JSC::Integrity::auditCellMinimally):
(JSC::Integrity::auditCellRandomly):
* tools/VMInspector.h:
(JSC::VMInspector::unusedVerifier):
(JSC::VMInspector::verifyCellSize):
* tools/VMInspectorInlines.h: Added.
(JSC::VMInspector::verifyCellSize):
(JSC::VMInspector::verifyCell):

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

23 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/heap/Heap.cpp
Source/JavaScriptCore/heap/SlotVisitor.cpp
Source/JavaScriptCore/runtime/ClassInfo.h
Source/JavaScriptCore/runtime/DirectArguments.h
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h
Source/JavaScriptCore/runtime/JSType.cpp
Source/JavaScriptCore/runtime/JSType.h
Source/JavaScriptCore/runtime/Options.cpp
Source/JavaScriptCore/runtime/OptionsList.h
Source/JavaScriptCore/runtime/Structure.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tools/Integrity.cpp [new file with mode: 0644]
Source/JavaScriptCore/tools/Integrity.h [new file with mode: 0644]
Source/JavaScriptCore/tools/IntegrityInlines.h [new file with mode: 0644]
Source/JavaScriptCore/tools/VMInspector.h
Source/JavaScriptCore/tools/VMInspectorInlines.h [new file with mode: 0644]

index a6c0f02..7ba99c2 100644 (file)
@@ -1001,6 +1001,11 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     runtime/WriteBarrier.h
     runtime/WriteBarrierInlines.h
 
+    tools/Integrity.h
+    tools/IntegrityInlines.h
+    tools/VMInspector.h
+    tools/VMInspectorInlines.h
+
     wasm/WasmCapabilities.h
     wasm/WasmCodeBlock.h
     wasm/WasmCompilationMode.h
index 9a905a4..9b706ff 100644 (file)
@@ -1,3 +1,171 @@
+2019-09-23  Mark Lam  <mark.lam@apple.com>
+
+        Introducing Integrity audit functions.
+        https://bugs.webkit.org/show_bug.cgi?id=202085
+
+        Reviewed by Saam Barati.
+
+        This patch's main goal is to introduce the Integrity audit functions.  They can
+        be used wherever we want to audit a cell to probabilistically ensure it is not
+        corrupted.  However, to keep this patch small, we will only introduce the audit
+        tool here with one example use in SlotVisitor.  We'll follow up later with more
+        patches to deploy this tool throughout the VM.
+
+        1. Introduced Integrity audit functions that can be configured at several
+           AuditLevels:
+               None - don't do any audits.
+               Minimal - do a minimal quick audit (minimize perf impact).
+               Full - do a full audit of the many aspects of a cell.
+               Random - randomly do a full audit with a probability dictated by
+                    Options::randomIntegrityAuditRate() between 0.0 (never audit) and
+                    1.0 (audit at every chance).
+
+           The default AuditLevel for Debug builds is Random.
+           The default AuditLevel for Release builds is None.
+           The default Options::randomIntegrityAuditRate() is 0.05.
+
+           How full audits work?
+           ====================
+           The full audit uses the VMInspector::verifyCell() template function to do its
+           job.  The reason for keeping this separate is to allow the template function
+           to be used later for debug checks that want to take some custom action on
+           verification failure instead of crashing with a RELEASE_ASSERT.
+
+           Full audit of a cell pointer includes:
+           a. Verify that a cell designated as a LargeAllocation is in the heap's
+              set of LargeAllocations.
+
+           b. Verify that a cell not designated as a LargeAllocation is actually in its
+              MarkedBlock's bounds.
+
+           c. Verify that the cell's container (LargeAllocation / MarkedBlock) actually
+              belongs to the current VM.
+
+           d. Verify that a cell in a MarkedBlock is properly aligned on the block's
+              allocation unit size.
+
+           e. If the cell is not an ImmutableButterfly, verify that it is not located in
+              the Gigacage.
+
+           f. Verify that the cell's JSType matches its StructureBlob's JSType.
+
+           g. Verify that the cell size as dictated by the cell ClassInfo does not exceed
+              the size of the allocation unit size (as expected by the container
+              MarkedBlock or LargeAllocation).
+
+              Some cells are dynamically size (see isDynamicallySizedType()).  For these
+              cells, we compute their sizes and verify that the size does not exceed the
+              allocation unit size.  Their sizes should also be greater or equal to the
+              static cell size as dictated by their ClassInfo.
+
+           h. If a cell has a butterfly, verify that the butterfly is in its the JSValue
+              Gigacage.
+
+           We can add more verifications later, or make some these more robust, but this
+           is a start for now.
+
+           How random audits work?
+           ======================
+           Random audits are triggered by the m_triggerBits bits in VM::m_integrityRandom.
+           m_triggerBits is a 64-bit bitfield.
+
+           If Options::randomIntegrityAuditRate() is 0, m_triggerBits will always be 0,
+           and no audits will be done.
+
+           If Options::randomIntegrityAuditRate() is non-zero, m_triggerBits will be
+           initialized as follows:
+
+                | 1 reload bit | ... 63 trigger bits ... |
+
+           The reload bit is always set (more details below).
+           Each of the 63 trigger bits are randomly set depending if the following is true
+           for the bit:
+
+                VM::random() <= Options::randomIntegrityAuditRate() * UINT_MAX
+
+           When Integrity::auditCell() is called, we take the bottom bit as the trigger
+           bit for the current cell, and shifts the rest down by 1.
+
+           If m_triggerBits is non-null after the shift, the taken trigger bit will dictate
+           whether we do a full audit on the current cell or not.
+
+           Once the reload bit reaches the bottom, we call a reload function to
+           re-initialize m_triggerBits.  The reload function also returns a bool
+           indicating whether to trigger a full audit of the current cell.
+
+           With this scheme, we only need to call the reload function once every 64 calls
+           to Integrity::auditCell(), and can efficiently determine whether to trigger
+           the audit the other 63 times with the probability specified in
+           Options::randomIntegrityAuditRate().
+
+        2. Embedded the C++ class size of JSCells into their ClassInfo.  This is used in
+           the full audits to verify cell sizes.
+
+        3. Added isDynamicallySizedType() to check if a JSType has a dynamic size allocation
+           i.e. the size of instances of this type is not determined by the static C++
+           size of its class, but rather, depends on some runtime variable.
+
+        4. Made the VMInspector a friend of several classes so that it can access their
+           private methods and fields.
+
+        5. Moved the inline function JSBigInt::allocationSize() from BigInt.cpp to its
+           header file so that we can use it in VMInspector::verifyCellSize().
+
+        6. Gave the JSModuleNamespaceObject() its own JSType so that we can identify it
+           as a dynamically sized object.
+
+        7. Increased the randomness of VM::random() (which is implemented with WeakRandom)
+           by re-seeding it with a cryptographically random number each GC.
+
+        8. Called Integrity::auditCell() on SlotVisitor::appendJSCellOrAuxiliary()'s cell
+           as an example use of auditCell().  More uses will be added in later patches to
+           follow.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * heap/Heap.cpp:
+        (JSC::Heap::runBeginPhase):
+        * heap/SlotVisitor.cpp:
+        (JSC::SlotVisitor::appendJSCellOrAuxiliary):
+        * runtime/ClassInfo.h:
+        * runtime/DirectArguments.h:
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::allocationSize): Deleted.
+        * runtime/JSBigInt.h:
+        (JSC::JSBigInt::allocationSize):
+        * runtime/JSModuleNamespaceObject.h:
+        * runtime/JSType.cpp:
+        (WTF::printInternal):
+        * runtime/JSType.h:
+        (JSC::isDynamicallySizedType):
+        * runtime/Options.cpp:
+        (JSC::recomputeDependentOptions):
+        * runtime/OptionsList.h:
+        * runtime/Structure.h:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        (JSC::VM::random):
+        (JSC::VM::integrityRandom):
+        * tools/Integrity.cpp: Added.
+        (JSC::Integrity::Random::Random):
+        (JSC::Integrity::Random::reloadAndCheckShouldAuditSlow):
+        (JSC::Integrity::auditCellFully):
+        (JSC::Integrity::auditCellMinimallySlow):
+        * tools/Integrity.h: Added.
+        (JSC::Integrity::auditCell):
+        * tools/IntegrityInlines.h: Added.
+        (JSC::Integrity::Random::shouldAudit):
+        (JSC::Integrity::auditCellMinimally):
+        (JSC::Integrity::auditCellRandomly):
+        * tools/VMInspector.h:
+        (JSC::VMInspector::unusedVerifier):
+        (JSC::VMInspector::verifyCellSize):
+        * tools/VMInspectorInlines.h: Added.
+        (JSC::VMInspector::verifyCellSize):
+        (JSC::VMInspector::verifyCell):
+
 2019-09-23  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r250262.
index 534d7d0..01b92db 100644 (file)
                FEA3BBAC212C97CB00E93AD1 /* DFGCFG.h in Headers */ = {isa = PBXBuildFile; fileRef = FEA3BBAB212C97CB00E93AD1 /* DFGCFG.h */; };
                FEB51F6C1A97B688001F921C /* Regress141809.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEB51F6B1A97B688001F921C /* Regress141809.mm */; };
                FEB58C15187B8B160098EF0B /* ErrorHandlingScope.h in Headers */ = {isa = PBXBuildFile; fileRef = FEB58C13187B8B160098EF0B /* ErrorHandlingScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               FEC5797323105B5100BCA83F /* VMInspectorInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = FEC5797223105B4800BCA83F /* VMInspectorInlines.h */; };
+               FEC5797623105F4E00BCA83F /* Integrity.h in Headers */ = {isa = PBXBuildFile; fileRef = FEC5797523105F4300BCA83F /* Integrity.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               FEC579782310954C00BCA83F /* IntegrityInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = FEC579772310954B00BCA83F /* IntegrityInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FECB8B271D25BB85006F2463 /* FunctionOverridesTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FECB8B251D25BB6E006F2463 /* FunctionOverridesTest.cpp */; };
                FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = FED287B115EC9A5700DA8161 /* LLIntOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = FED94F2C171E3E2300BE77A4 /* Watchdog.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FEB51F6B1A97B688001F921C /* Regress141809.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Regress141809.mm; path = API/tests/Regress141809.mm; sourceTree = "<group>"; };
                FEB58C12187B8B160098EF0B /* ErrorHandlingScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlingScope.cpp; sourceTree = "<group>"; };
                FEB58C13187B8B160098EF0B /* ErrorHandlingScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ErrorHandlingScope.h; sourceTree = "<group>"; };
+               FEC5797223105B4800BCA83F /* VMInspectorInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VMInspectorInlines.h; sourceTree = "<group>"; };
+               FEC5797423105F4200BCA83F /* Integrity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Integrity.cpp; sourceTree = "<group>"; };
+               FEC5797523105F4300BCA83F /* Integrity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Integrity.h; sourceTree = "<group>"; };
+               FEC579772310954B00BCA83F /* IntegrityInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntegrityInlines.h; sourceTree = "<group>"; };
                FECB8B251D25BB6E006F2463 /* FunctionOverridesTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FunctionOverridesTest.cpp; path = API/tests/FunctionOverridesTest.cpp; sourceTree = "<group>"; };
                FECB8B261D25BB6E006F2463 /* FunctionOverridesTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FunctionOverridesTest.h; path = API/tests/FunctionOverridesTest.h; sourceTree = "<group>"; };
                FECB8B291D25CABB006F2463 /* testapi-function-overrides.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = "testapi-function-overrides.js"; path = "API/tests/testapi-function-overrides.js"; sourceTree = "<group>"; };
                                FEA0C4011CDD7D0E00481991 /* FunctionWhitelist.h */,
                                FE1BD0221E72052F00134BC9 /* HeapVerifier.cpp */,
                                FE1BD0231E72052F00134BC9 /* HeapVerifier.h */,
+                               FEC5797423105F4200BCA83F /* Integrity.cpp */,
+                               FEC5797523105F4300BCA83F /* Integrity.h */,
+                               FEC579772310954B00BCA83F /* IntegrityInlines.h */,
                                FE384EE11ADDB7AD0055DE2C /* JSDollarVM.cpp */,
                                FE384EE21ADDB7AD0055DE2C /* JSDollarVM.h */,
                                86B5822C14D22F5F00A9C306 /* ProfileTreeNode.h */,
                                86B5826A14D35D5100A9C306 /* TieredMMapArray.h */,
                                FE3022D41E42856700BAC493 /* VMInspector.cpp */,
                                FE3022D51E42856700BAC493 /* VMInspector.h */,
+                               FEC5797223105B4800BCA83F /* VMInspectorInlines.h */,
                        );
                        path = tools;
                        sourceTree = "<group>";
                                0FEC3C601F379F5300F59B6C /* CagedBarrierPtr.h in Headers */,
                                BC18C3ED0E16F5CD00B34460 /* CallData.h in Headers */,
                                0F64B27A1A7957B2006E4E66 /* CallEdge.h in Headers */,
+                               FEC579782310954C00BCA83F /* IntegrityInlines.h in Headers */,
                                796DAA2B1E89CCD6005DF24A /* CalleeBits.h in Headers */,
                                1429D8DE0ED2205B00B89619 /* CallFrame.h in Headers */,
                                FEA3BBA8212B655900E93AD1 /* CallFrameInlines.h in Headers */,
                                7905BB691D12050E0019FE57 /* InlineAccess.h in Headers */,
                                148A7BF01B82975A002D9157 /* InlineCallFrame.h in Headers */,
                                0F24E55617F0B71C00ABB217 /* InlineCallFrameSet.h in Headers */,
+                               FEC5797623105F4E00BCA83F /* Integrity.h in Headers */,
                                A584032018BFFBE1005A0811 /* InspectorAgent.h in Headers */,
                                A593CF7F1840362C00BFCE27 /* InspectorAgentBase.h in Headers */,
                                A593CF87184038CA00BFCE27 /* InspectorAgentRegistry.h in Headers */,
                                0FF729BE166AD360000F5BA3 /* ProfilerExecutionCounter.h in Headers */,
                                0F190CAD189D82F6000AE5F0 /* ProfilerJettisonReason.h in Headers */,
                                0FF729BF166AD360000F5BA3 /* ProfilerOrigin.h in Headers */,
+                               FEC5797323105B5100BCA83F /* VMInspectorInlines.h in Headers */,
                                0FF729C0166AD360000F5BA3 /* ProfilerOriginStack.h in Headers */,
                                0FB1058C1675483300F8AB6E /* ProfilerOSRExit.h in Headers */,
                                0FB1058E1675483A00F8AB6E /* ProfilerOSRExitSite.h in Headers */,
index 43ea530..ba86676 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2018 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
@@ -989,6 +989,7 @@ tools/CompilerTimingScope.cpp
 tools/FunctionOverrides.cpp
 tools/FunctionWhitelist.cpp
 tools/HeapVerifier.cpp
+tools/Integrity.cpp
 tools/JSDollarVM.cpp
 tools/SigillCrashAnalyzer.cpp
 tools/VMInspector.cpp
index 67ee196..151f60d 100644 (file)
@@ -80,6 +80,7 @@
 #include "WeakMapImplInlines.h"
 #include "WeakSetInlines.h"
 #include <algorithm>
+#include <wtf/CryptographicallyRandomNumber.h>
 #include <wtf/ListDump.h>
 #include <wtf/MainThread.h>
 #include <wtf/ParallelVectorIterator.h>
@@ -1268,6 +1269,9 @@ NEVER_INLINE bool Heap::runBeginPhase(GCConductor conn)
 
     m_beforeGC = MonotonicTime::now();
 
+    if (!Options::seedOfVMRandomForFuzzer())
+        vm().random().setSeed(cryptographicallyRandomNumber());
+
     if (m_collectionScope) {
         dataLog("Collection scope already set during GC: ", *m_collectionScope, "\n");
         RELEASE_ASSERT_NOT_REACHED();
index 84afa45..e0e9071 100644 (file)
@@ -33,6 +33,7 @@
 #include "HeapAnalyzer.h"
 #include "HeapCellInlines.h"
 #include "HeapProfiler.h"
+#include "IntegrityInlines.h"
 #include "JSArray.h"
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
@@ -221,6 +222,7 @@ void SlotVisitor::appendJSCellOrAuxiliary(HeapCell* heapCell)
     
         JSCell* jsCell = static_cast<JSCell*>(heapCell);
         validateCell(jsCell);
+        Integrity::auditCell(vm(), jsCell);
         
         jsCell->setCellState(CellState::PossiblyGrey);
 
index cbbe15a..3957bc9 100644 (file)
@@ -185,7 +185,8 @@ struct MethodTable {
         &ClassName::estimatedSize, \
         &ClassName::visitOutputConstraints, \
     }, \
-    ClassName::TypedArrayStorageType
+    ClassName::TypedArrayStorageType, \
+    sizeof(ClassName)
 
 struct ClassInfo {
     // A string denoting the class name. Example: "Window".
@@ -221,6 +222,7 @@ struct ClassInfo {
     MethodTable methodTable;
 
     TypedArrayType typedArrayStorageType;
+    unsigned staticClassSize;
 };
 
 } // namespace JSC
index d474140..d9a1031 100644 (file)
@@ -184,6 +184,8 @@ private:
     uint32_t m_minCapacity; // The max of this and length determines the capacity of this object. It may be the actual capacity, or maybe something smaller. We arrange it this way to be kind to the JITs.
     using MappedArguments = CagedBarrierPtr<Gigacage::Primitive, bool>;
     MappedArguments m_mappedArguments; // If non-null, it means that length, callee, and caller are fully materialized properties.
+
+    friend class VMInspector;
 };
 
 } // namespace JSC
index a3041eb..330b96d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2017 Caio Lima <ticaiolima@gmail.com>
- * Copyright (C) 2017-2018 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
@@ -83,12 +83,6 @@ JSBigInt* JSBigInt::createZero(VM& vm)
     return zeroBigInt;
 }
 
-inline size_t JSBigInt::allocationSize(unsigned length)
-{
-    size_t sizeWithPadding = WTF::roundUpToMultipleOf<sizeof(size_t)>(sizeof(JSBigInt));
-    return sizeWithPadding + length * sizeof(Digit);
-}
-
 JSBigInt* JSBigInt::tryCreateWithLength(ExecState* exec, unsigned length)
 {
     VM& vm = exec->vm();
index 254f2fc..b7661c7 100644 (file)
@@ -233,7 +233,7 @@ private:
 
     static Optional<Digit> toShiftAmount(JSBigInt* x);
 
-    static size_t allocationSize(unsigned length);
+    inline static size_t allocationSize(unsigned length);
     inline static size_t offsetOfData()
     {
         return WTF::roundUpToMultipleOf<sizeof(Digit)>(sizeof(JSBigInt));
@@ -249,8 +249,16 @@ private:
 
     const unsigned m_length;
     bool m_sign { false };
+
+    friend class VMInspector;
 };
 
+inline size_t JSBigInt::allocationSize(unsigned length)
+{
+    size_t sizeWithPadding = WTF::roundUpToMultipleOf<sizeof(size_t)>(sizeof(JSBigInt));
+    return sizeWithPadding + length * sizeof(Digit);
+}
+
 inline JSBigInt* asBigInt(JSValue value)
 {
     ASSERT(value.asCell()->isBigInt());
index 23245c8..614cc6a 100644 (file)
@@ -59,7 +59,7 @@ public:
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ModuleNamespaceObjectType, StructureFlags), info());
     }
 
     AbstractModuleRecord* moduleRecord() { return m_moduleRecord.get(); }
@@ -103,6 +103,8 @@ private:
     ExportMap m_exports;
     Vector<Identifier> m_names;
     WriteBarrier<AbstractModuleRecord> m_moduleRecord;
+
+    friend class VMInspector;
 };
 
 } // namespace JSC
index dac5e69..502b46e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2018-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
@@ -94,6 +94,7 @@ void printInternal(PrintStream& out, JSC::JSType type)
     CASE(ModuleEnvironmentType)
     CASE(StrictEvalActivationType)
     CASE(WithScopeType)
+    CASE(ModuleNamespaceObjectType)
     CASE(RegExpObjectType)
     CASE(ProxyObjectType)
     CASE(JSGeneratorType)
index 01e7022..7a50e88 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2006-2018 Apple Inc. All rights reserved.
+ *  Copyright (C) 2006-2019 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -106,6 +106,7 @@ enum JSType : uint8_t {
     WithScopeType,
     // End JSScope types.
 
+    ModuleNamespaceObjectType,
     RegExpObjectType,
     ProxyObjectType,
     JSGeneratorType,
@@ -139,6 +140,18 @@ inline constexpr bool isTypedArrayType(JSType type)
     return (static_cast<uint32_t>(type) - FirstTypedArrayType) < NumberOfTypedArrayTypesExcludingDataView;
 }
 
+inline constexpr bool isDynamicallySizedType(JSType type)
+{
+    if (type == BigIntType
+        || type == DirectArgumentsType
+        || type == FinalObjectType
+        || type == LexicalEnvironmentType
+        || type == ModuleEnvironmentType
+        || type == ModuleNamespaceObjectType)
+        return true;
+    return false;
+}
+
 } // namespace JSC
 
 namespace WTF {
index b0a242b..5dafe81 100644 (file)
@@ -504,6 +504,11 @@ static void recomputeDependentOptions()
 
     if (!Options::useCodeCache())
         Options::diskCachePath() = nullptr;
+
+    if (Options::randomIntegrityAuditRate() < 0)
+        Options::randomIntegrityAuditRate() = 0;
+    else if (Options::randomIntegrityAuditRate() > 1.0)
+        Options::randomIntegrityAuditRate() = 1.0;
 }
 
 void Options::initialize()
index 0b9fb83..079a579 100644 (file)
@@ -344,6 +344,7 @@ namespace JSC {
     \
     v(Bool, alwaysGeneratePCToCodeOriginMap, false, Normal, "This will make sure we always generate a PCToCodeOriginMap for JITed code.") \
     \
+    v(Double, randomIntegrityAuditRate, 0.05, Normal, "Probability of random integrity audits [0.0 - 1.0]") \
     v(Bool, verifyHeap, false, Normal, nullptr) \
     v(Unsigned, numberOfGCCyclesToRecordForVerification, 3, Normal, nullptr) \
     \
@@ -366,6 +367,7 @@ namespace JSC {
     v(Unsigned, fireOSRExitFuzzAt, 0, Normal, nullptr) \
     v(Unsigned, fireOSRExitFuzzAtOrAfter, 0, Normal, nullptr) \
     \
+    v(Unsigned, seedOfVMRandomForFuzzer, 0, Normal, "0 means not fuzzing this; use a cryptographically random seed") \
     v(Bool, useRandomizingFuzzerAgent, false, Normal, nullptr) \
     v(Unsigned, seedOfRandomizingFuzzerAgent, 1, Normal, nullptr) \
     v(Bool, dumpRandomizingFuzzerAgentPredictions, false, Normal, nullptr) \
index 03a7829..33f1a7c 100644 (file)
@@ -786,6 +786,8 @@ private:
     PropertyOffset m_offset;
 
     uint32_t m_propertyHash;
+
+    friend class VMInspector;
 };
 
 } // namespace JSC
index 4394c85..adb5a91 100644 (file)
@@ -251,6 +251,8 @@ VM::VM(VMType vmType, HeapType heapType)
 #if USE(CF)
     , m_runLoop(CFRunLoopGetCurrent())
 #endif // USE(CF)
+    , m_random(Options::seedOfVMRandomForFuzzer() ? Options::seedOfVMRandomForFuzzer() : cryptographicallyRandomNumber())
+    , m_integrityRandom(*this)
     , heap(*this, heapType)
     , fastMallocAllocator(makeUnique<FastMallocAlignedMemoryAllocator>())
     , primitiveGigacageAllocator(makeUnique<GigacageAlignedMemoryAllocator>(Gigacage::Primitive))
index 5d8e621..39bea5d 100644 (file)
@@ -40,6 +40,7 @@
 #include "FunctionHasExecutedCache.h"
 #include "FuzzerAgent.h"
 #include "Heap.h"
+#include "Integrity.h"
 #include "Intrinsic.h"
 #include "IsoCellSet.h"
 #include "IsoSubspace.h"
@@ -310,6 +311,9 @@ public:
     // Global object in which execution began.
     JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject(const CallFrame*) const;
 
+    WeakRandom& random() { return m_random; }
+    Integrity::Random& integrityRandom() { return m_integrityRandom; }
+
 private:
     unsigned nextID();
 
@@ -322,6 +326,9 @@ private:
     RetainPtr<CFRunLoopRef> m_runLoop;
 #endif
 
+    WeakRandom m_random;
+    Integrity::Random m_integrityRandom;
+
 public:
     Heap heap;
     
diff --git a/Source/JavaScriptCore/tools/Integrity.cpp b/Source/JavaScriptCore/tools/Integrity.cpp
new file mode 100644 (file)
index 0000000..355b5d6
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 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
+ * 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 "IntegrityInlines.h"
+
+#include "Options.h"
+#include "VMInspectorInlines.h"
+
+namespace JSC {
+namespace Integrity {
+
+namespace {
+constexpr bool verbose = false;
+}
+
+Random::Random(VM& vm)
+{
+    reloadAndCheckShouldAuditSlow(vm);
+}
+
+bool Random::reloadAndCheckShouldAuditSlow(VM& vm)
+{
+    auto locker = holdLock(m_lock);
+
+    if (!Options::randomIntegrityAuditRate()) {
+        m_triggerBits = 0; // Never trigger, and don't bother reloading.
+        if (verbose)
+            dataLogLn("disabled Integrity audits: trigger bits ", RawPointer(reinterpret_cast<void*>(m_triggerBits)));
+        return false;
+    }
+
+    // Reload the trigger bits.
+    m_triggerBits = 1ull << 63;
+
+    uint32_t threshold = UINT_MAX * Options::randomIntegrityAuditRate();
+    for (int i = 0; i < numberOfTriggerBits; ++i) {
+        bool trigger = vm.random().getUint32() <= threshold;
+        m_triggerBits = m_triggerBits | (static_cast<uint64_t>(trigger) << i);
+    }
+    if (verbose)
+        dataLogLn("reloaded Integrity trigger bits ", RawPointer(reinterpret_cast<void*>(m_triggerBits)));
+    ASSERT(m_triggerBits >= (1ull << 63));
+    return vm.random().getUint32() <= threshold;
+}
+
+void auditCellFully(VM& vm, JSCell* cell)
+{
+    VMInspector::verifyCell<VMInspector::ReleaseAssert>(vm, cell);
+}
+
+void auditCellMinimallySlow(VM&, JSCell* cell)
+{
+    if (Gigacage::contains(cell)) {
+        if (cell->type() != JSImmutableButterflyType) {
+            if (verbose)
+                dataLogLn("Bad cell ", RawPointer(cell), " ", JSValue(cell));
+            CRASH();
+        }
+    }
+}
+
+} // namespace Integrity
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/Integrity.h b/Source/JavaScriptCore/tools/Integrity.h
new file mode 100644 (file)
index 0000000..3d0d24f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 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
+ * 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 "JSCJSValue.h"
+#include <wtf/Gigacage.h>
+#include <wtf/Lock.h>
+
+namespace JSC {
+
+class JSCell;
+class VM;
+
+namespace Integrity {
+
+enum class AuditLevel {
+    None,
+    Minimal,
+    Full,
+    Random,
+};
+
+#ifdef NDEBUG
+static constexpr AuditLevel DefaultAuditLevel = AuditLevel::None;
+#else
+static constexpr AuditLevel DefaultAuditLevel = AuditLevel::Random;
+#endif
+
+class Random {
+public:
+    Random(VM&);
+
+    ALWAYS_INLINE bool shouldAudit(VM&);
+
+private:
+    JS_EXPORT_PRIVATE bool reloadAndCheckShouldAuditSlow(VM&);
+
+    uint64_t m_triggerBits;
+    Lock m_lock;
+
+    // The top bit is reserved as a termination bit. Hence, the number of
+    // trigger bits is always 1 less than will fit in m_triggerBits.
+    static constexpr int numberOfTriggerBits = (sizeof(m_triggerBits) * CHAR_BIT) - 1;
+};
+
+ALWAYS_INLINE void auditCellRandomly(VM&, JSCell*);
+ALWAYS_INLINE void auditCellMinimally(VM&, JSCell*);
+JS_EXPORT_PRIVATE void auditCellMinimallySlow(VM&, JSCell*);
+JS_EXPORT_PRIVATE void auditCellFully(VM&, JSCell*);
+
+template<AuditLevel = AuditLevel::Random, typename T>
+ALWAYS_INLINE void auditCell(VM&, T) { }
+
+template<AuditLevel auditLevel = DefaultAuditLevel>
+ALWAYS_INLINE void auditCell(VM& vm, JSCell* cell)
+{
+    switch (auditLevel) {
+    case AuditLevel::None:
+        return;
+    case AuditLevel::Minimal:
+        return auditCellMinimally(vm, cell);
+    case AuditLevel::Full:
+        return auditCellFully(vm, cell);
+    case AuditLevel::Random:
+        return auditCellRandomly(vm, cell);
+    }
+}
+
+template<AuditLevel auditLevel = DefaultAuditLevel>
+ALWAYS_INLINE void auditCell(VM& vm, JSValue value)
+{
+    if (auditLevel == AuditLevel::None)
+        return;
+
+    if (value.isCell())
+        auditCell<auditLevel>(vm, value.asCell());
+}
+
+} // namespace Integrity
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/IntegrityInlines.h b/Source/JavaScriptCore/tools/IntegrityInlines.h
new file mode 100644 (file)
index 0000000..bc1f30d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 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
+ * 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 "Integrity.h"
+#include "VM.h"
+
+namespace JSC {
+namespace Integrity {
+
+ALWAYS_INLINE bool Random::shouldAudit(VM& vm)
+{
+    // If auditing is enabled, then the top bit of m_triggerBits is always set
+    // to 1 on reload. When this top bit reaches the bottom, it does not
+    // indicate that we should trigger an audit but rather that we've shifted
+    // out all the available trigger bits and hence, need to reload. Instead,
+    // reloadAndCheckShouldAuditSlow() will return whether we actually need to
+    // trigger an audit this turn.
+    //
+    // This function can be called concurrently from different threads and can
+    // be racy. For that reason, we intentionally do not write back to
+    // m_triggerBits if newTriggerBits is null. This ensures that if
+    // Options::randomIntegrityAuditRate() is non-zero, then m_triggerBits will
+    // always have at least 1 bit to trigger a reload.
+
+    uint64_t newTriggerBits = m_triggerBits;
+    bool shouldAudit = newTriggerBits & 1;
+    newTriggerBits = newTriggerBits >> 1;
+    if (LIKELY(!shouldAudit)) {
+        m_triggerBits = newTriggerBits;
+        return false;
+    }
+
+    if (!newTriggerBits)
+        return reloadAndCheckShouldAuditSlow(vm);
+
+    m_triggerBits = newTriggerBits;
+    return true;
+}
+
+ALWAYS_INLINE void auditCellMinimally(VM& vm, JSCell* cell)
+{
+    if (UNLIKELY(Gigacage::contains(cell)))
+        auditCellMinimallySlow(vm, cell);
+}
+
+ALWAYS_INLINE void auditCellRandomly(VM& vm, JSCell* cell)
+{
+    if (UNLIKELY(vm.integrityRandom().shouldAudit(vm)))
+        auditCellFully(vm, cell);
+}
+
+} // namespace Integrity
+} // namespace JSC
index 35b4c13..d4efb78 100644 (file)
@@ -80,6 +80,17 @@ public:
     JS_EXPORT_PRIVATE static void dumpCellMemoryToStream(JSCell*, PrintStream&);
     JS_EXPORT_PRIVATE static void dumpSubspaceHashes(VM*);
 
+    enum VerifierAction { ReleaseAssert, Custom };
+
+    using VerifyFunctor = bool(bool condition, const char* description, ...);
+    static bool unusedVerifier(bool, const char*, ...) { return false; }
+
+    template<VerifierAction, VerifyFunctor = unusedVerifier>
+    static bool verifyCellSize(VM&, JSCell*, size_t allocatorCellSize);
+
+    template<VerifierAction, VerifyFunctor = unusedVerifier>
+    static bool verifyCell(VM&, JSCell*);
+
 private:
     template <typename Functor> void iterate(const Functor& functor)
     {
diff --git a/Source/JavaScriptCore/tools/VMInspectorInlines.h b/Source/JavaScriptCore/tools/VMInspectorInlines.h
new file mode 100644 (file)
index 0000000..000d908
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 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
+ * 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 "DirectArguments.h"
+#include "JSBigInt.h"
+#include "JSLexicalEnvironment.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleNamespaceObject.h"
+#include "VMInspector.h"
+#include <wtf/Assertions.h>
+
+namespace JSC {
+
+#define AUDIT_CONDITION(x) (x), #x
+#define AUDIT_VERIFY(action, verifier, cond, ...) do { \
+        if (action == VerifierAction::ReleaseAssert) \
+            RELEASE_ASSERT(cond, __VA_ARGS__); \
+        else if (!verifier(AUDIT_CONDITION(cond), __VA_ARGS__)) \
+            return false; \
+    } while (false)
+
+template<VMInspector::VerifierAction action, VMInspector::VerifyFunctor verifier>
+bool VMInspector::verifyCellSize(VM& vm, JSCell* cell, size_t allocatorCellSize)
+{
+    Structure* structure = cell->structure(vm);
+    const ClassInfo* classInfo = structure->classInfo();
+    JSType cellType = cell->type();
+    AUDIT_VERIFY(action, verifier, cellType == structure->m_blob.type(), cell, cellType, structure->m_blob.type());
+
+    if (isDynamicallySizedType(cellType)) {
+        size_t cellSize = 0;
+        switch (cellType) {
+        case BigIntType: {
+            auto* bigInt = jsCast<JSBigInt*>(cell);
+            cellSize = JSBigInt::allocationSize(bigInt->length());
+            break;
+        }
+        case DirectArgumentsType: {
+            auto* args = jsCast<DirectArguments*>(cell);
+            cellSize = DirectArguments::allocationSize(args->m_minCapacity);
+            break;
+        }
+        case FinalObjectType:
+            cellSize = JSFinalObject::allocationSize(structure->inlineCapacity());
+            break;
+        case LexicalEnvironmentType: {
+            auto* env = jsCast<JSLexicalEnvironment*>(cell);
+            cellSize = JSLexicalEnvironment::allocationSize(env->symbolTable());
+            break;
+        }
+        case ModuleEnvironmentType: {
+            auto* env = jsCast<JSModuleEnvironment*>(cell);
+            cellSize = JSModuleEnvironment::allocationSize(env->symbolTable());
+            break;
+        }
+        case ModuleNamespaceObjectType: {
+            auto* obj = jsCast<JSModuleNamespaceObject*>(cell);
+            cellSize = JSModuleNamespaceObject::allocationSize(obj->m_names.capacity());
+            break;
+        }
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+        AUDIT_VERIFY(action, verifier, cellSize <= allocatorCellSize, cell, cellType, cellSize, allocatorCellSize);
+        AUDIT_VERIFY(action, verifier, cellSize >= classInfo->staticClassSize, cell, cellType, cellSize, classInfo->staticClassSize);
+    } else
+        AUDIT_VERIFY(action, verifier, classInfo->staticClassSize <= allocatorCellSize, cell, cellType, classInfo->staticClassSize, allocatorCellSize);
+
+    return true;
+}
+
+template<VMInspector::VerifierAction action, VMInspector::VerifyFunctor verifier>
+bool VMInspector::verifyCell(VM& vm, JSCell* cell)
+{
+    size_t allocatorCellSize = 0;
+    if (cell->isLargeAllocation()) {
+        LargeAllocation& largeAllocation = cell->largeAllocation();
+        AUDIT_VERIFY(action, verifier, &largeAllocation.vm() == &vm, cell, cell->type(), &largeAllocation.vm(), &vm);
+
+        bool isValidLargeAllocation = false;
+        for (auto* i : vm.heap.objectSpace().largeAllocations()) {
+            if (i == &largeAllocation) {
+                isValidLargeAllocation = true;
+                break;
+            }
+        }
+        AUDIT_VERIFY(action, verifier, isValidLargeAllocation, cell, cell->type());
+
+        allocatorCellSize = largeAllocation.cellSize();
+    } else {
+        MarkedBlock& block = cell->markedBlock();
+        MarkedBlock::Handle& blockHandle = block.handle();
+        AUDIT_VERIFY(action, verifier, &block.vm() == &vm, cell, cell->type(), &block.vm(), &vm);
+
+        uintptr_t blockStartAddress = reinterpret_cast<uintptr_t>(blockHandle.start());
+        AUDIT_VERIFY(action, verifier, blockHandle.contains(cell), cell, cell->type(), blockStartAddress, blockHandle.end());
+
+        uintptr_t cellAddress = reinterpret_cast<uintptr_t>(cell);
+        uintptr_t cellOffset = cellAddress - blockStartAddress;
+        allocatorCellSize = block.cellSize();
+        bool cellIsProperlyAligned = !(cellOffset % allocatorCellSize);
+        AUDIT_VERIFY(action, verifier, cellIsProperlyAligned, cell, cell->type(), allocatorCellSize);
+    }
+
+    auto cellType = cell->type();
+    if (cell->type() != JSImmutableButterflyType)
+        AUDIT_VERIFY(action, verifier, !Gigacage::contains(cell), cell, cellType);
+
+    if (!verifyCellSize<action, verifier>(vm, cell, allocatorCellSize))
+        return false;
+
+    if (Gigacage::isEnabled(Gigacage::JSValue) && cell->isObject()) {
+        JSObject* object = asObject(cell);
+        const Butterfly* butterfly = object->butterfly();
+        AUDIT_VERIFY(action, verifier, !butterfly || Gigacage::isCaged(Gigacage::JSValue, butterfly), cell, cell->type(), butterfly);
+    }
+
+    return true;
+}
+
+#undef AUDIT_VERIFY
+#undef AUDIT_CONDITION
+
+} // namespace JSC