Ignore:
Timestamp:
May 10, 2022, 2:55:45 PM (3 years ago)
Author:
[email protected]
Message:

Add optional Integrity checks at JSC API boundaries.
https://bugs.webkit.org/show_bug.cgi?id=240264

Reviewed by Yusuke Suzuki.

  1. Defined ENABLE_EXTRA_INTEGRITY_CHECKS in Integrity.h. JSC developers can enable this for their local build if they want to enable more prolific Integrity audits. This is disabled by default.

This feature is currently only supported for USE(JSVALUE64) targets.

The following changes only take effect if ENABLE(EXTRA_INTEGRITY_CHECKS) is enabled.
Otherwise, these are no-ops.

  1. Added Integrity audits to all toJS and toRef conversion functions in APICast.h. This will help us detect if bad values are passed across the API boundary.
  1. Added some Integrity audits in JSValue.mm where the APICast ones were insufficient.

The following changes are in effect even when ENABLE(EXTRA_INTEGRITY_CHECKS) is
disabled. Some of these were made to support ENABLE(EXTRA_INTEGRITY_CHECKS), and
some are just clean up in related code that I had to touch along the way.

  1. Moved isSanePointer() to Integrity.h so that it can be used in more places.
  1. Changed VM registration with the VMInspector so that it's registered earlier and removed later. Integrity audits may need to audit VM pointers while the VM is being constructed and destructed.
  1. Added VM::m_isInService to track when the VM is fully constructed or about to be destructed since the VM is now registered with the VMInspector differently (see (4) above). Applied this check in places that need it.
  1. Fixed VMInspector::isValidExecutableMemory() to check the ExecutableAllocator directly without iterating VMs (which is completely unnecessary).
  1. Fixed VMInspector::isValidExecutableMemory() and VMInspector::codeBlockForMachinePC() to use AdoptLock. This fixes a race condition where the lock can be contended after ensureIsSafeToLock() succeeds.
  1. Added VMInspector::isValidVM() to check if a VM pointer is registered or not. VMInspector caches the most recently added or found VM so that isValidVM() can just check the cache for its fast path.
  1. Moved the implementation of VMInspector::verifyCell() to Integrity::analyzeCell()

and add more checks to it. VMInspector::verifyCell() now calls Integrity::verifyCell()
which uses Integrity::analyzeCell() to do the real cell analysis.

  1. Also strengten Integrity::auditStructureID() so that it will check if a

Structure's memory has been released. This change is enabled on Debug builds
by default as well as when ENABLE(EXTRA_INTEGRITY_CHECKS). It is disabled
on Release builds.

  • API/APICast.h:

(toJS):
(toJSForGC):
(uncheckedToJS):
(toRef):
(toGlobalRef):

  • API/JSContext.mm:
  • API/JSContextRef.cpp:
  • API/JSScript.mm:
  • API/JSValue.mm:

(ObjcContainerConvertor::convert):
(objectToValueWithoutCopy):
(objectToValue):

  • API/JSVirtualMachine.mm:
  • API/JSWeakPrivate.cpp:
  • API/glib/JSCContext.cpp:
  • API/glib/JSCWrapperMap.cpp:
  • API/tests/JSObjectGetProxyTargetTest.cpp:
  • bytecode/SpeculatedType.cpp:

(JSC::speculationFromCell):
(JSC::isSanePointer): Deleted.

  • heap/HeapFinalizerCallback.cpp:
  • heap/WeakSet.h:
  • runtime/Structure.h:
  • runtime/VM.cpp:

(JSC::VM::VM):
(JSC::VM::~VM):

  • runtime/VM.h:

(JSC::VM::isInService const):

  • tools/HeapVerifier.cpp:

(JSC::HeapVerifier::checkIfRecorded):

  • tools/Integrity.cpp:

(JSC::Integrity::Random::reloadAndCheckShouldAuditSlow):
(JSC::Integrity::auditCellMinimallySlow):
(JSC::Integrity::doAudit):
(JSC::Integrity::Analyzer::analyzeVM):
(JSC::Integrity::Analyzer::analyzeCell):
(JSC::Integrity::doAuditSlow):
(JSC::Integrity::verifyCell):
(): Deleted.
(JSC::Integrity::auditCellFully): Deleted.

  • tools/Integrity.h:

(JSC::isSanePointer):
(JSC::Integrity::auditCell):
(JSC::Integrity::audit):

  • tools/IntegrityInlines.h:

(JSC::Integrity::auditCell):
(JSC::Integrity::auditCellFully):
(JSC::Integrity::auditStructureID):
(JSC::Integrity::doAudit):

  • tools/VMInspector.cpp:

(JSC::VMInspector::add):
(JSC::VMInspector::remove):
(JSC::VMInspector::isValidVMSlow):
(JSC::VMInspector::dumpVMs):
(JSC::VMInspector::isValidExecutableMemory):
(JSC::VMInspector::codeBlockForMachinePC):
(JSC::ensureIsSafeToLock): Deleted.

  • tools/VMInspector.h:

(JSC::VMInspector::isValidVM):
(): Deleted.
(JSC::VMInspector::unusedVerifier): Deleted.

  • tools/VMInspectorInlines.h:

(JSC::VMInspector::verifyCell):
(JSC::VMInspector::verifyCellSize): Deleted.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/tools/Integrity.h

    r288815 r294017  
    11/*
    2  * Copyright (C) 2019-2021 Apple Inc. All rights reserved.
     2 * Copyright (C) 2019-2022 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2626#pragma once
    2727
    28 #include "JSCJSValue.h"
    29 #include "StructureID.h"
    30 #include <wtf/Gigacage.h>
     28#include <wtf/Assertions.h>
    3129#include <wtf/Lock.h>
    3230
     31#if OS(DARWIN)
     32#include <mach/vm_param.h>
     33#endif
     34
     35#if USE(JSVALUE32)
     36#define ENABLE_EXTRA_INTEGRITY_CHECKS 0 // Not supported.
     37#else
     38// Force ENABLE_EXTRA_INTEGRITY_CHECKS to 1 for your local build if you want
     39// more prolific audits to be enabled.
     40#define ENABLE_EXTRA_INTEGRITY_CHECKS 0
     41#endif
     42
     43// From API/JSBase.h
     44typedef const struct OpaqueJSContextGroup* JSContextGroupRef;
     45typedef const struct OpaqueJSContext* JSContextRef;
     46typedef struct OpaqueJSContext* JSGlobalContextRef;
     47typedef struct OpaqueJSPropertyNameAccumulator* JSPropertyNameAccumulatorRef;
     48typedef const struct OpaqueJSValue* JSValueRef;
     49typedef struct OpaqueJSValue* JSObjectRef;
     50
    3351namespace JSC {
    3452
    3553class JSCell;
     54class JSGlobalObject;
     55class JSObject;
     56class JSValue;
     57class Structure;
     58class StructureID;
    3659class VM;
    3760
     
    6891};
    6992
     93ALWAYS_INLINE static bool isSanePointer(const void* pointer)
     94{
     95#if CPU(ADDRESS64)
     96    uintptr_t pointerAsInt = bitwise_cast<uintptr_t>(pointer);
     97    uintptr_t canonicalPointerBits = pointerAsInt << (64 - OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH));
     98    uintptr_t nonCanonicalPointerBits = pointerAsInt >> OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH);
     99    return !nonCanonicalPointerBits && canonicalPointerBits;
     100#else
     101    UNUSED_PARAM(pointer);
     102    return true;
     103#endif
     104}
     105
     106#if USE(JSVALUE64)
     107
     108class Analyzer {
     109public:
     110    enum Action { LogOnly, LogAndCrash };
     111
     112    static bool analyzeVM(VM&, Action);
     113    static bool analyzeCell(VM&, JSCell*, Action);
     114    static bool analyzeCell(JSCell*, Action);
     115};
     116
     117JS_EXPORT_PRIVATE JSContextRef doAudit(JSContextRef);
     118JS_EXPORT_PRIVATE JSGlobalContextRef doAudit(JSGlobalContextRef);
     119JS_EXPORT_PRIVATE JSObjectRef doAudit(JSObjectRef);
     120JS_EXPORT_PRIVATE JSValueRef doAudit(JSValueRef);
     121
     122JS_EXPORT_PRIVATE JSValue doAudit(JSValue);
     123JS_EXPORT_PRIVATE JSCell* doAudit(JSCell*);
     124JS_EXPORT_PRIVATE JSCell* doAudit(VM&, JSCell*);
     125JS_EXPORT_PRIVATE JSObject* doAudit(JSObject*);
     126JS_EXPORT_PRIVATE JSGlobalObject* doAudit(JSGlobalObject*);
     127
     128VM* doAudit(VM*); // see IntegrityInlines.h
     129
     130// These are used for debugging queries, and will not crash.
     131JS_EXPORT_PRIVATE bool verifyCell(JSCell*);
     132JS_EXPORT_PRIVATE bool verifyCell(VM&, JSCell*);
     133
     134#endif // USE(JSVALUE64)
     135
    70136ALWAYS_INLINE void auditCellRandomly(VM&, JSCell*);
    71137ALWAYS_INLINE void auditCellMinimally(VM&, JSCell*);
    72138JS_EXPORT_PRIVATE void auditCellMinimallySlow(VM&, JSCell*);
    73 JS_EXPORT_PRIVATE void auditCellFully(VM&, JSCell*);
     139ALWAYS_INLINE void auditCellFully(VM&, JSCell*);
    74140
    75141template<AuditLevel = AuditLevel::Random, typename T>
     
    79145ALWAYS_INLINE void auditCell(VM& vm, JSCell* cell)
    80146{
    81     switch (auditLevel) {
    82     case AuditLevel::None:
     147    static_assert(auditLevel == AuditLevel::None || auditLevel == AuditLevel::Minimal || auditLevel == AuditLevel::Full || auditLevel == AuditLevel::Random);
     148
     149    UNUSED_PARAM(vm);
     150    UNUSED_PARAM(cell);
     151    if constexpr (auditLevel == AuditLevel::None)
    83152        return;
    84     case AuditLevel::Minimal:
     153    if constexpr (auditLevel == AuditLevel::Minimal)
    85154        return auditCellMinimally(vm, cell);
    86     case AuditLevel::Full:
     155    if constexpr (auditLevel == AuditLevel::Full)
    87156        return auditCellFully(vm, cell);
    88     case AuditLevel::Random:
     157    if constexpr (auditLevel == AuditLevel::Random)
    89158        return auditCellRandomly(vm, cell);
    90     }
    91159}
    92160
    93161template<AuditLevel auditLevel = DefaultAuditLevel>
    94 ALWAYS_INLINE void auditCell(VM& vm, JSValue value)
    95 {
    96     if (auditLevel == AuditLevel::None)
    97         return;
    98 
    99     if (value.isCell())
    100         auditCell<auditLevel>(vm, value.asCell());
    101 }
     162ALWAYS_INLINE void auditCell(VM&, JSValue);
    102163
    103164ALWAYS_INLINE void auditStructureID(StructureID);
    104165
     166#if ENABLE(EXTRA_INTEGRITY_CHECKS) && USE(JSVALUE64)
     167template<typename T> ALWAYS_INLINE T audit(T value) { return doAudit(value); }
     168#else
     169template<typename T> ALWAYS_INLINE T audit(T value) { return value; }
     170#endif
     171
     172#if COMPILER(MSVC) || !VA_OPT_SUPPORTED
     173
     174#define IA_LOG(assertion, format, ...) do { \
     175        WTFLogAlways("Integrity ERROR: %s @ %s:%d\n", #assertion, __FILE__, __LINE__); \
     176        WTFLogAlways("    " format, ##__VA_ARGS__); \
     177    } while (false)
     178
     179#define IA_ASSERT_WITH_ACTION(assertion, action, ...) do { \
     180        if (UNLIKELY(!(assertion))) { \
     181            IA_LOG(assertion, __VA_ARGS__); \
     182            WTFReportBacktraceWithPrefix("    "); \
     183            action; \
     184        } \
     185    } while (false)
     186
     187#define IA_ASSERT(assertion, ...) \
     188    IA_ASSERT_WITH_ACTION(assertion, { \
     189        RELEASE_ASSERT((assertion), ##__VA_ARGS__); \
     190    }, ## __VA_ARGS__)
     191
     192#else // not (COMPILER(MSVC) || !VA_OPT_SUPPORTED)
     193
     194#define IA_LOG(assertion, format, ...) do { \
     195        WTFLogAlways("Integrity ERROR: %s @ %s:%d\n", #assertion, __FILE__, __LINE__); \
     196        WTFLogAlways("    " format __VA_OPT__(,) __VA_ARGS__); \
     197    } while (false)
     198
     199#define IA_ASSERT_WITH_ACTION(assertion, action, ...) do { \
     200        if (UNLIKELY(!(assertion))) { \
     201            IA_LOG(assertion, __VA_ARGS__); \
     202            WTFReportBacktraceWithPrefix("    "); \
     203            action; \
     204        } \
     205    } while (false)
     206
     207#define IA_ASSERT(assertion, ...) \
     208    IA_ASSERT_WITH_ACTION(assertion, { \
     209        RELEASE_ASSERT((assertion) __VA_OPT__(,) __VA_ARGS__); \
     210    } __VA_OPT__(,) __VA_ARGS__)
     211
     212#endif // COMPILER(MSVC) || !VA_OPT_SUPPORTED
     213
    105214} // namespace Integrity
    106215
Note: See TracChangeset for help on using the changeset viewer.