Changeset 294017 in webkit


Ignore:
Timestamp:
May 10, 2022 2:55:45 PM (2 years ago)
Author:
mark.lam@apple.com
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.

Location:
trunk/Source/JavaScriptCore
Files:
24 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/APICast.h

    r292929 r294017  
    11/*
    2  * Copyright (C) 2006-2019 Apple Inc.  All rights reserved.
     2 * Copyright (C) 2006-2022 Apple Inc.  All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727#define APICast_h
    2828
     29#include "Integrity.h"
    2930#include "JSAPIValueWrapper.h"
    3031#include "JSCJSValue.h"
     
    5354{
    5455    ASSERT(context);
    55     return reinterpret_cast<JSC::JSGlobalObject*>(const_cast<OpaqueJSContext*>(context));
     56    return JSC::Integrity::audit(reinterpret_cast<JSC::JSGlobalObject*>(const_cast<OpaqueJSContext*>(context)));
    5657}
    5758
     
    5960{
    6061    ASSERT(context);
    61     return reinterpret_cast<JSC::JSGlobalObject*>(context);
     62    return JSC::Integrity::audit(reinterpret_cast<JSC::JSGlobalObject*>(context));
    6263}
    6364
     
    8485    if (!result)
    8586        return JSC::jsNull();
    86     if (result.isCell())
     87    if (result.isCell()) {
     88        JSC::Integrity::audit(result.asCell());
    8789        RELEASE_ASSERT(result.asCell()->methodTable());
     90    }
    8891    return result;
    8992}
     
    9295inline JSC::JSValue toJS(JSValueRef value)
    9396{
    94     return bitwise_cast<JSC::JSValue>(value);
     97    return JSC::Integrity::audit(bitwise_cast<JSC::JSValue>(value));
    9598}
    9699#endif
     
    107110    JSC::JSValue result = bitwise_cast<JSC::JSValue>(v);
    108111#endif
    109     if (result && result.isCell())
     112    if (result && result.isCell()) {
     113        JSC::Integrity::audit(result.asCell());
    110114        RELEASE_ASSERT(result.asCell()->methodTable());
     115    }
    111116    return result;
    112117}
     
    115120inline JSC::JSObject* uncheckedToJS(JSObjectRef o)
    116121{
    117     return reinterpret_cast<JSC::JSObject*>(o);
     122    return JSC::Integrity::audit(reinterpret_cast<JSC::JSObject*>(o));
    118123}
    119124
     
    133138inline JSC::VM* toJS(JSContextGroupRef g)
    134139{
    135     return reinterpret_cast<JSC::VM*>(const_cast<OpaqueJSContextGroup*>(g));
     140    return JSC::Integrity::audit(reinterpret_cast<JSC::VM*>(const_cast<OpaqueJSContextGroup*>(g)));
    136141}
    137142
     
    147152#else
    148153    UNUSED_PARAM(vm);
    149     return bitwise_cast<JSValueRef>(v);
     154    return bitwise_cast<JSValueRef>(JSC::Integrity::audit(v));
    150155#endif
    151156}
     
    159164inline JSValueRef toRef(JSC::JSValue v)
    160165{
    161     return bitwise_cast<JSValueRef>(v);
     166    return bitwise_cast<JSValueRef>(JSC::Integrity::audit(v));
    162167}
    163168#endif
     
    165170inline JSObjectRef toRef(JSC::JSObject* o)
    166171{
    167     return reinterpret_cast<JSObjectRef>(o);
     172    return reinterpret_cast<JSObjectRef>(JSC::Integrity::audit(o));
    168173}
    169174
    170175inline JSObjectRef toRef(const JSC::JSObject* o)
    171176{
    172     return reinterpret_cast<JSObjectRef>(const_cast<JSC::JSObject*>(o));
     177    return reinterpret_cast<JSObjectRef>(JSC::Integrity::audit(const_cast<JSC::JSObject*>(o)));
    173178}
    174179
    175180inline JSContextRef toRef(JSC::JSGlobalObject* globalObject)
    176181{
    177     return reinterpret_cast<JSContextRef>(globalObject);
     182    return reinterpret_cast<JSContextRef>(JSC::Integrity::audit(globalObject));
    178183}
    179184
    180185inline JSGlobalContextRef toGlobalRef(JSC::JSGlobalObject* globalObject)
    181186{
    182     return reinterpret_cast<JSGlobalContextRef>(globalObject);
     187    return reinterpret_cast<JSGlobalContextRef>(JSC::Integrity::audit(globalObject));
    183188}
    184189
     
    190195inline JSContextGroupRef toRef(JSC::VM* g)
    191196{
    192     return reinterpret_cast<JSContextGroupRef>(g);
     197    return reinterpret_cast<JSContextGroupRef>(JSC::Integrity::audit(g));
    193198}
    194199
  • trunk/Source/JavaScriptCore/API/JSContext.mm

    r293879 r294017  
    11/*
    2  * Copyright (C) 2013-2021 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2022 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2828#import "APICast.h"
    2929#import "Completion.h"
     30#import "IntegrityInlines.h"
    3031#import "JSBaseInternal.h"
    3132#import "JSCInlines.h"
  • trunk/Source/JavaScriptCore/API/JSContextRef.cpp

    r293779 r294017  
    3131#include "CallFrame.h"
    3232#include "InitializeThreading.h"
     33#include "IntegrityInlines.h"
    3334#include "JSAPIGlobalObject.h"
    3435#include "JSAPIWrapperObject.h"
  • trunk/Source/JavaScriptCore/API/JSScript.mm

    r291779 r294017  
    11/*
    2  * Copyright (C) 2019-2020 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
     
    3232#import "CodeCache.h"
    3333#import "Identifier.h"
     34#import "IntegrityInlines.h"
    3435#import "JSContextInternal.h"
    3536#import "JSScriptSourceProvider.h"
  • trunk/Source/JavaScriptCore/API/JSValue.mm

    r293703 r294017  
    11/*
    2  * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2022 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    5959#if JSC_OBJC_API_ENABLED
    6060
     61using JSC::Integrity::audit;
     62
    6163NSString * const JSPropertyDescriptorWritableKey = @"writable";
    6264NSString * const JSPropertyDescriptorEnumerableKey = @"enumerable";
     
    971973    auto it = m_objectMap.find(object);
    972974    if (it != m_objectMap.end())
    973         return it->value;
     975        return audit(it->value);
    974976
    975977    ObjcContainerConvertor::Task task = objectToValueWithoutCopy(m_context, object);
    976978    add(task);
    977     return task.js;
     979    return audit(task.js);
    978980}
    979981
     
    10051007static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context, id object)
    10061008{
    1007     JSGlobalContextRef contextRef = [context JSGlobalContextRef];
     1009    JSGlobalContextRef contextRef = audit([context JSGlobalContextRef]);
    10081010
    10091011    if (!object)
     
    10571059    ObjcContainerConvertor::Task task = objectToValueWithoutCopy(context, object);
    10581060    if (task.type == ContainerNone)
    1059         return task.js;
     1061        return audit(task.js);
    10601062
    10611063    JSC::JSLockHolder locker(toJS(contextRef));
     
    10881090    } while (!convertor.isWorkListEmpty());
    10891091
    1090     return task.js;
     1092    return audit(task.js);
    10911093}
    10921094
  • trunk/Source/JavaScriptCore/API/JSVirtualMachine.mm

    r279179 r294017  
    11/*
    2  * Copyright (C) 2013-2021 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2022 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3131
    3232#import "APICast.h"
     33#import "IntegrityInlines.h"
    3334#import "JITWorklist.h"
    3435#import "JSManagedValueInternal.h"
  • trunk/Source/JavaScriptCore/API/JSWeakPrivate.cpp

    r261755 r294017  
    11/*
    2  * Copyright (C) 2017 Apple Inc.  All rights reserved.
     2 * Copyright (C) 2017-2022 Apple Inc.  All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2828
    2929#include "APICast.h"
     30#include "IntegrityInlines.h"
    3031#include "Weak.h"
    3132#include <wtf/ThreadSafeRefCounted.h>
  • trunk/Source/JavaScriptCore/API/glib/JSCContext.cpp

    r292487 r294017  
    2222
    2323#include "APICast.h"
     24#include "IntegrityInlines.h"
    2425#include "JSCClassPrivate.h"
    2526#include "JSCContextInternal.h"
  • trunk/Source/JavaScriptCore/API/glib/JSCWrapperMap.cpp

    r293324 r294017  
    2222
    2323#include "APICast.h"
     24#include "IntegrityInlines.h"
    2425#include "JSAPIWrapperGlobalObject.h"
    2526#include "JSAPIWrapperObject.h"
  • trunk/Source/JavaScriptCore/API/tests/JSObjectGetProxyTargetTest.cpp

    r273777 r294017  
    11/*
    2  * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017-2022 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2828
    2929#include "APICast.h"
     30#include "IntegrityInlines.h"
    3031#include "JSCInlines.h"
    3132#include "JSObjectRefPrivate.h"
  • trunk/Source/JavaScriptCore/ChangeLog

    r294000 r294017  
     12022-05-10  Mark Lam  <mark.lam@apple.com>
     2
     3        Add optional Integrity checks at JSC API boundaries.
     4        https://bugs.webkit.org/show_bug.cgi?id=240264
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        1. Defined ENABLE_EXTRA_INTEGRITY_CHECKS in Integrity.h.  JSC developers can enable
     9           this for their local build if they want to enable more prolific Integrity audits.
     10           This is disabled by default.
     11
     12           This feature is currently only supported for USE(JSVALUE64) targets.
     13
     14        The following changes only take effect if ENABLE(EXTRA_INTEGRITY_CHECKS) is enabled.
     15        Otherwise, these are no-ops.
     16
     17        2. Added Integrity audits to all toJS and toRef conversion functions in APICast.h.
     18           This will help us detect if bad values are passed across the API boundary.
     19
     20        3. Added some Integrity audits in JSValue.mm where the APICast ones were insufficient.
     21
     22        The following changes are in effect even when ENABLE(EXTRA_INTEGRITY_CHECKS) is
     23        disabled.  Some of these were made to support ENABLE(EXTRA_INTEGRITY_CHECKS), and
     24        some are just clean up in related code that I had to touch along the way.
     25
     26        4. Moved isSanePointer() to Integrity.h so that it can be used in more places.
     27
     28        5. Changed VM registration with the VMInspector so that it's registered earlier
     29           and removed later.  Integrity audits may need to audit VM pointers while the
     30           VM is being constructed and destructed.
     31
     32        6. Added VM::m_isInService to track when the VM is fully constructed or about to
     33           be destructed since the VM is now registered with the VMInspector differently
     34           (see (4) above).  Applied this check in places that need it.
     35
     36        7. Fixed VMInspector::isValidExecutableMemory() to check the ExecutableAllocator
     37           directly without iterating VMs (which is completely unnecessary).
     38
     39        8. Fixed VMInspector::isValidExecutableMemory() and VMInspector::codeBlockForMachinePC()
     40           to use AdoptLock.  This fixes a race condition where the lock can be contended
     41           after ensureIsSafeToLock() succeeds.
     42
     43        9. Added VMInspector::isValidVM() to check if a VM pointer is registered or not.
     44           VMInspector caches the most recently added or found VM so that isValidVM()
     45           can just check the cache for its fast path.
     46
     47        10. Moved the implementation of VMInspector::verifyCell() to Integrity::analyzeCell()
     48            and add more checks to it.  VMInspector::verifyCell() now calls Integrity::verifyCell()
     49            which uses Integrity::analyzeCell() to do the real cell analysis.
     50
     51        11. Also strengten Integrity::auditStructureID() so that it will check if a
     52            Structure's memory has been released.  This change is enabled on Debug builds
     53            by default as well as when ENABLE(EXTRA_INTEGRITY_CHECKS).  It is disabled
     54            on Release builds.
     55
     56        * API/APICast.h:
     57        (toJS):
     58        (toJSForGC):
     59        (uncheckedToJS):
     60        (toRef):
     61        (toGlobalRef):
     62        * API/JSContext.mm:
     63        * API/JSContextRef.cpp:
     64        * API/JSScript.mm:
     65        * API/JSValue.mm:
     66        (ObjcContainerConvertor::convert):
     67        (objectToValueWithoutCopy):
     68        (objectToValue):
     69        * API/JSVirtualMachine.mm:
     70        * API/JSWeakPrivate.cpp:
     71        * API/glib/JSCContext.cpp:
     72        * API/glib/JSCWrapperMap.cpp:
     73        * API/tests/JSObjectGetProxyTargetTest.cpp:
     74        * bytecode/SpeculatedType.cpp:
     75        (JSC::speculationFromCell):
     76        (JSC::isSanePointer): Deleted.
     77        * heap/HeapFinalizerCallback.cpp:
     78        * heap/WeakSet.h:
     79        * runtime/Structure.h:
     80        * runtime/VM.cpp:
     81        (JSC::VM::VM):
     82        (JSC::VM::~VM):
     83        * runtime/VM.h:
     84        (JSC::VM::isInService const):
     85        * tools/HeapVerifier.cpp:
     86        (JSC::HeapVerifier::checkIfRecorded):
     87        * tools/Integrity.cpp:
     88        (JSC::Integrity::Random::reloadAndCheckShouldAuditSlow):
     89        (JSC::Integrity::auditCellMinimallySlow):
     90        (JSC::Integrity::doAudit):
     91        (JSC::Integrity::Analyzer::analyzeVM):
     92        (JSC::Integrity::Analyzer::analyzeCell):
     93        (JSC::Integrity::doAuditSlow):
     94        (JSC::Integrity::verifyCell):
     95        (): Deleted.
     96        (JSC::Integrity::auditCellFully): Deleted.
     97        * tools/Integrity.h:
     98        (JSC::isSanePointer):
     99        (JSC::Integrity::auditCell):
     100        (JSC::Integrity::audit):
     101        * tools/IntegrityInlines.h:
     102        (JSC::Integrity::auditCell):
     103        (JSC::Integrity::auditCellFully):
     104        (JSC::Integrity::auditStructureID):
     105        (JSC::Integrity::doAudit):
     106        * tools/VMInspector.cpp:
     107        (JSC::VMInspector::add):
     108        (JSC::VMInspector::remove):
     109        (JSC::VMInspector::isValidVMSlow):
     110        (JSC::VMInspector::dumpVMs):
     111        (JSC::VMInspector::isValidExecutableMemory):
     112        (JSC::VMInspector::codeBlockForMachinePC):
     113        (JSC::ensureIsSafeToLock): Deleted.
     114        * tools/VMInspector.h:
     115        (JSC::VMInspector::isValidVM):
     116        (): Deleted.
     117        (JSC::VMInspector::unusedVerifier): Deleted.
     118        * tools/VMInspectorInlines.h:
     119        (JSC::VMInspector::verifyCell):
     120        (JSC::VMInspector::verifyCellSize): Deleted.
     121
    11222022-05-09  Ross Kirsling  <ross.kirsling@sony.com>
    2123
  • trunk/Source/JavaScriptCore/bytecode/SpeculatedType.cpp

    r293657 r294017  
    11/*
    2  * Copyright (C) 2011-2020 Apple Inc. All rights reserved.
     2 * Copyright (C) 2011-2022 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3232#include "DateInstance.h"
    3333#include "DirectArguments.h"
     34#include "Integrity.h"
    3435#include "JSArray.h"
    3536#include "JSBigInt.h"
     
    565566}
    566567
    567 ALWAYS_INLINE static bool isSanePointer(const void* pointer)
    568 {
    569     // FIXME: rdar://69036888: remove this when no longer needed.
    570 #if CPU(ADDRESS64)
    571     uintptr_t pointerAsInt = bitwise_cast<uintptr_t>(pointer);
    572     uintptr_t canonicalPointerBits = pointerAsInt << (64 - OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH));
    573     uintptr_t nonCanonicalPointerBits = pointerAsInt >> OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH);
    574     return !nonCanonicalPointerBits && canonicalPointerBits;
    575 #else
    576     UNUSED_PARAM(pointer);
    577     return true;
    578 #endif
    579 }
    580 
    581568SpeculatedType speculationFromCell(JSCell* cell)
    582569{
    583     if (UNLIKELY(!isSanePointer(cell))) {
     570    // FIXME: rdar://69036888: remove isSanePointer checks when no longer needed.
     571    if (UNLIKELY(!Integrity::isSanePointer(cell))) {
    584572        ASSERT_NOT_REACHED();
    585573        return SpecNone;
     
    588576        JSString* string = jsCast<JSString*>(cell);
    589577        if (const StringImpl* impl = string->tryGetValueImpl()) {
    590             if (UNLIKELY(!isSanePointer(impl))) {
     578            if (UNLIKELY(!Integrity::isSanePointer(impl))) {
    591579                ASSERT_NOT_REACHED();
    592580                return SpecNone;
     
    599587    // FIXME: rdar://69036888: undo this when no longer needed.
    600588    auto* structure = cell->structureID().tryDecode();
    601     if (UNLIKELY(!isSanePointer(structure))) {
     589    if (UNLIKELY(!Integrity::isSanePointer(structure))) {
    602590        ASSERT_NOT_REACHED();
    603591        return SpecNone;
  • trunk/Source/JavaScriptCore/heap/HeapFinalizerCallback.cpp

    r216689 r294017  
    11/*
    2  * Copyright (C) 2017 Apple Inc.  All rights reserved.
     2 * Copyright (C) 2017-2022 Apple Inc.  All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2828
    2929#include "APICast.h"
     30#include "IntegrityInlines.h"
    3031
    3132namespace JSC {
  • trunk/Source/JavaScriptCore/heap/WeakSet.h

    r273138 r294017  
    11/*
    2  * Copyright (C) 2012-2021 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012-2022 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3535class WeakImpl;
    3636
     37namespace Integrity {
     38class Analyzer;
     39}
     40
    3741class WeakSet : public BasicRawSentinelNode<WeakSet> {
    3842    friend class LLIntOffsetsExtractor;
     43    friend class Integrity::Analyzer;
    3944
    4045public:
  • trunk/Source/JavaScriptCore/runtime/Structure.h

    r293710 r294017  
    7070struct HashTableValue;
    7171
     72namespace Integrity {
     73class Analyzer;
     74}
     75
    7276// The out-of-line property storage capacity to use when first allocating out-of-line
    7377// storage. Note that all objects start out without having any out-of-line storage;
     
    991995    friend class VMInspector;
    992996    friend class JSDollarVMHelper;
     997    friend class Integrity::Analyzer;
    993998};
    994999
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r292929 r294017  
    242242        CRASH_WITH_INFO(0x4242424220202020, 0xbadbeef0badbeef, 0x1234123412341234, 0x1337133713371337);
    243243
     244    VMInspector::instance().add(this);
     245
    244246    interpreter = new Interpreter(*this);
    245247    updateSoftReservedZoneSize(Options::softReservedZoneSize());
     
    402404#endif
    403405
    404     VMInspector::instance().add(this);
    405 
    406406    if (!g_jscConfig.disabledFreezingForTesting)
    407407        Config::permanentlyFreeze();
     408
     409    // We must set this at the end only after the VM is fully initialized.
     410    WTF::storeStoreFence();
     411    m_isInService = true;
    408412}
    409413
     
    428432        m_watchdog->willDestroyVM(this);
    429433    m_traps.willDestroyVM();
    430     VMInspector::instance().remove(this);
     434    m_isInService = false;
     435    WTF::storeStoreFence();
    431436
    432437    // Never GC, ever again.
     
    456461
    457462    JSRunLoopTimer::Manager::shared().unregisterVM(*this);
    458    
     463
     464    VMInspector::instance().remove(this);
     465
    459466    delete interpreter;
    460467#ifndef NDEBUG
  • trunk/Source/JavaScriptCore/runtime/VM.h

    r292795 r294017  
    347347    GCClient::Heap clientHeap;
    348348
     349    bool isInService() const { return m_isInService; }
     350
    349351    const HeapCellType& cellHeapCellType() { return heap.cellHeapCellType; }
    350352    const JSDestructibleObjectHeapCellType& destructibleObjectHeapCellType() { return heap.destructibleObjectHeapCellType; };
     
    946948    unsigned m_typeProfilerEnabledCount;
    947949    bool m_needToFirePrimitiveGigacageEnabled { false };
     950    bool m_isInService { false };
    948951    Lock m_scratchBufferLock;
    949952    Vector<ScratchBuffer*> m_scratchBuffers;
  • trunk/Source/JavaScriptCore/tools/HeapVerifier.cpp

    r293779 r294017  
    444444    Locker locker { AdoptLock, inspector.getLock() };
    445445    inspector.iterate([&] (VM& vm) {
     446        if (!vm.isInService())
     447            return IterationStatus::Continue;
     448
    446449        if (!vm.heap.m_verifier)
    447450            return IterationStatus::Continue;
  • trunk/Source/JavaScriptCore/tools/Integrity.cpp

    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
     
    2727#include "Integrity.h"
    2828
     29#include "APICast.h"
     30#include "CellSize.h"
     31#include "IntegrityInlines.h"
     32#include "JSCast.h"
    2933#include "JSCellInlines.h"
     34#include "JSGlobalObject.h"
    3035#include "Options.h"
    3136#include "VMInspectorInlines.h"
     
    3439namespace Integrity {
    3540
    36 namespace {
    37 constexpr bool verbose = false;
     41namespace IntegrityInternal {
     42static constexpr bool verbose = false;
    3843}
    3944
     
    4954    if (!Options::randomIntegrityAuditRate()) {
    5055        m_triggerBits = 0; // Never trigger, and don't bother reloading.
    51         if (verbose)
     56        if (IntegrityInternal::verbose)
    5257            dataLogLn("disabled Integrity audits: trigger bits ", RawPointer(reinterpret_cast<void*>(m_triggerBits)));
    5358        return false;
     
    6267        m_triggerBits = m_triggerBits | (static_cast<uint64_t>(trigger) << i);
    6368    }
    64     if (verbose)
     69    if (IntegrityInternal::verbose)
    6570        dataLogLn("reloaded Integrity trigger bits ", RawPointer(reinterpret_cast<void*>(m_triggerBits)));
    6671    ASSERT(m_triggerBits >= (1ull << 63));
     
    6873}
    6974
    70 void auditCellFully(VM& vm, JSCell* cell)
    71 {
    72     VMInspector::verifyCell<VMInspector::ReleaseAssert>(vm, cell);
    73 }
    74 
    7575void auditCellMinimallySlow(VM&, JSCell* cell)
    7676{
    7777    if (Gigacage::contains(cell)) {
    7878        if (cell->type() != JSImmutableButterflyType) {
    79             if (verbose)
    80                 dataLogLn("Bad cell ", RawPointer(cell), " ", JSValue(cell));
     79            if (IntegrityInternal::verbose)
     80                dataLogLn("Integrity ERROR: Bad cell ", RawPointer(cell), " ", JSValue(cell));
    8181            CRASH();
    8282        }
     
    8484}
    8585
     86#if USE(JSVALUE64)
     87
     88JSContextRef doAudit(JSContextRef ctx)
     89{
     90    IA_ASSERT(ctx, "NULL JSContextRef");
     91    toJS(ctx); // toJS will trigger an audit.
     92    return ctx;
     93}
     94
     95JSGlobalContextRef doAudit(JSGlobalContextRef ctx)
     96{
     97    IA_ASSERT(ctx, "NULL JSGlobalContextRef");
     98    toJS(ctx); // toJS will trigger an audit.
     99    return ctx;
     100}
     101
     102JSObjectRef doAudit(JSObjectRef objectRef)
     103{
     104    if (!objectRef)
     105        return objectRef;
     106    toJS(objectRef); // toJS will trigger an audit.
     107    return objectRef;
     108}
     109
     110JSValueRef doAudit(JSValueRef valueRef)
     111{
     112#if CPU(ADDRESS64)
     113    if (!valueRef)
     114        return valueRef;
     115    toJS(valueRef); // toJS will trigger an audit.
     116#endif
     117    return valueRef;
     118}
     119
     120JSValue doAudit(JSValue value)
     121{
     122    if (value.isCell())
     123        doAudit(value.asCell());
     124    return value;
     125}
     126
     127bool Analyzer::analyzeVM(VM& vm, Analyzer::Action action)
     128{
     129    IA_ASSERT_WITH_ACTION(VMInspector::isValidVM(&vm), {
     130        VMInspector::dumpVMs();
     131        if (action == Action::LogAndCrash)
     132            RELEASE_ASSERT(VMInspector::isValidVM(&vm));
     133        else
     134            return false;
     135    }, "Invalid VM %p", &vm);
     136    return true;
     137}
     138
     139#if COMPILER(MSVC) || !VA_OPT_SUPPORTED
     140
     141#define AUDIT_VERIFY(cond, format, ...) do { \
     142        IA_ASSERT_WITH_ACTION(cond, { \
     143            WTFLogAlways("    cell %p", cell); \
     144            if (action == Action::LogAndCrash) \
     145                RELEASE_ASSERT((cond), ##__VA_ARGS__); \
     146            else \
     147                return false; \
     148        }, format, ##__VA_ARGS__); \
     149    } while (false)
     150
     151#else // not (COMPILER(MSVC) || !VA_OPT_SUPPORTED)
     152
     153#define AUDIT_VERIFY(cond, format, ...) do { \
     154        IA_ASSERT_WITH_ACTION(cond, { \
     155            WTFLogAlways("    cell %p", cell); \
     156            if (action == Action::LogAndCrash) \
     157                RELEASE_ASSERT((cond) __VA_OPT__(,) __VA_ARGS__); \
     158            else \
     159                return false; \
     160        }, format __VA_OPT__(,) __VA_ARGS__); \
     161    } while (false)
     162
     163#endif // COMPILER(MSVC) || !VA_OPT_SUPPORTED
     164
     165bool Analyzer::analyzeCell(VM& vm, JSCell* cell, Analyzer::Action action)
     166{
     167    AUDIT_VERIFY(isSanePointer(cell), "cell %p cell.type %d", cell, cell->type());
     168
     169    size_t allocatorCellSize = 0;
     170    if (cell->isPreciseAllocation()) {
     171        PreciseAllocation& preciseAllocation = cell->preciseAllocation();
     172        AUDIT_VERIFY(&preciseAllocation.vm() == &vm,
     173            "cell %p cell.type %d preciseAllocation.vm %p vm %p", cell, cell->type(), &preciseAllocation.vm(), &vm);
     174
     175        bool isValidPreciseAllocation = false;
     176        for (auto* i : vm.heap.objectSpace().preciseAllocations()) {
     177            if (i == &preciseAllocation) {
     178                isValidPreciseAllocation = true;
     179                break;
     180            }
     181        }
     182        AUDIT_VERIFY(isValidPreciseAllocation, "cell %p cell.type %d", cell, cell->type());
     183
     184        allocatorCellSize = preciseAllocation.cellSize();
     185    } else {
     186        MarkedBlock& block = cell->markedBlock();
     187        MarkedBlock::Handle& blockHandle = block.handle();
     188        AUDIT_VERIFY(&block.vm() == &vm,
     189            "cell %p cell.type %d markedBlock.vm %p vm %p", cell, cell->type(), &block.vm(), &vm);
     190
     191        uintptr_t blockStartAddress = reinterpret_cast<uintptr_t>(blockHandle.start());
     192        AUDIT_VERIFY(blockHandle.contains(cell),
     193            "cell %p cell.type %d markedBlock.start %p markedBlock.end %p", cell, cell->type(), blockHandle.start(), blockHandle.end());
     194
     195        uintptr_t cellAddress = reinterpret_cast<uintptr_t>(cell);
     196        uintptr_t cellOffset = cellAddress - blockStartAddress;
     197        allocatorCellSize = block.cellSize();
     198        bool cellIsProperlyAligned = !(cellOffset % allocatorCellSize);
     199        AUDIT_VERIFY(cellIsProperlyAligned,
     200            "cell %p cell.type %d allocator.cellSize %zu", cell, cell->type(), allocatorCellSize);
     201    }
     202
     203    JSType cellType = cell->type();
     204    if (cell->type() != JSImmutableButterflyType)
     205        AUDIT_VERIFY(!Gigacage::contains(cell), "cell %p cell.type %d", cell, cellType);
     206
     207    WeakSet& weakSet = cell->cellContainer().weakSet();
     208    AUDIT_VERIFY(!weakSet.m_allocator || isSanePointer(weakSet.m_allocator),
     209        "cell %p cell.type %d weakSet.allocator %p", cell, cell->type(), weakSet.m_allocator);
     210    AUDIT_VERIFY(!weakSet.m_nextAllocator || isSanePointer(weakSet.m_nextAllocator),
     211        "cell %p cell.type %d weakSet.allocator %p", cell, cell->type(), weakSet.m_nextAllocator);
     212
     213    // If we're currently destructing the cell, then we can't rely on its
     214    // structure being good. Skip the following tests which rely on structure.
     215    if (vm.currentlyDestructingCallbackObject == cell)
     216        return true;
     217
     218    auto structureID = cell->structureID();
     219
     220    Structure* structure = structureID.tryDecode();
     221    AUDIT_VERIFY(structure,
     222        "cell %p cell.type %d structureID.bits 0x%x", cell, cellType, structureID.bits());
     223    if (action == Analyzer::Action::LogAndCrash) {
     224        // structure should be pointing to readable memory. Force a read.
     225        WTF::opaque(*bitwise_cast<uintptr_t*>(structure));
     226    }
     227
     228    const ClassInfo* classInfo = structure->classInfoForCells();
     229    AUDIT_VERIFY(cellType == structure->m_blob.type(),
     230        "cell %p cell.type %d structureBlob.type %d", cell, cellType, structure->m_blob.type());
     231
     232    size_t size = cellSize(cell);
     233    AUDIT_VERIFY(size <= allocatorCellSize,
     234        "cell %p cell.type %d cell.size %zu allocator.cellSize %zu, classInfo.cellSize %u", cell, cellType, size, allocatorCellSize, classInfo->staticClassSize);
     235    if (isDynamicallySizedType(cellType)) {
     236        AUDIT_VERIFY(size >= classInfo->staticClassSize,
     237            "cell %p cell.type %d cell.size %zu classInfo.cellSize %u", cell, cellType, size, classInfo->staticClassSize);
     238    }
     239
     240    if (cell->isObject()) {
     241        AUDIT_VERIFY(jsDynamicCast<JSObject*>(cell),
     242            "cell %p cell.type %d", cell, cell->type());
     243
     244        if (Gigacage::isEnabled(Gigacage::JSValue)) {
     245            JSObject* object = bitwise_cast<JSObject*>(cell);
     246            const Butterfly* butterfly = object->butterfly();
     247            AUDIT_VERIFY(!butterfly || Gigacage::isCaged(Gigacage::JSValue, butterfly),
     248                "cell %p cell.type %d butterfly %p", cell, cell->type(), butterfly);
     249        }
     250    }
     251
     252    return true;
     253}
     254
     255bool Analyzer::analyzeCell(JSCell* cell, Analyzer::Action action)
     256{
     257    if (!cell)
     258        return cell;
     259
     260    JSValue value = JSValue::decode(static_cast<EncodedJSValue>(bitwise_cast<uintptr_t>(cell)));
     261    AUDIT_VERIFY(value.isCell(), "Invalid cell address: cell %p", cell);
     262
     263    VM& vm = cell->vm();
     264    analyzeVM(vm, action);
     265    return analyzeCell(vm, cell, action);
     266}
     267
     268#undef AUDIT_VERIFY
     269
     270VM* doAuditSlow(VM* vm)
     271{
     272    Analyzer::analyzeVM(*vm, Analyzer::Action::LogAndCrash);
     273    return vm;
     274}
     275
     276JSCell* doAudit(JSCell* cell)
     277{
     278    Analyzer::analyzeCell(cell, Analyzer::Action::LogAndCrash);
     279    return cell;
     280}
     281
     282JSCell* doAudit(VM& vm, JSCell* cell)
     283{
     284    if (!cell)
     285        return cell;
     286    Analyzer::analyzeCell(vm, cell, Analyzer::Action::LogAndCrash);
     287    return cell;
     288}
     289
     290bool verifyCell(JSCell* cell)
     291{
     292    bool valid = Analyzer::analyzeCell(cell, Analyzer::Action::LogOnly);
     293    WTFLogAlways("Cell %p is %s", cell, valid ? "VALID" : "INVALID");
     294    return valid;
     295}
     296
     297bool verifyCell(VM& vm, JSCell* cell)
     298{
     299    bool valid = Analyzer::analyzeCell(vm, cell, Analyzer::Action::LogOnly);
     300    WTFLogAlways("Cell %p is %s", cell, valid ? "VALID" : "INVALID");
     301    return valid;
     302}
     303
     304JSObject* doAudit(JSObject* object)
     305{
     306    if (!object)
     307        return object;
     308    JSCell* cell = doAudit(reinterpret_cast<JSCell*>(object));
     309    IA_ASSERT(cell->isObject(), "Invalid JSObject %p", object);
     310    return object;
     311}
     312
     313JSGlobalObject* doAudit(JSGlobalObject* globalObject)
     314{
     315    doAudit(reinterpret_cast<JSCell*>(globalObject));
     316    IA_ASSERT(globalObject->isGlobalObject(), "Invalid JSGlobalObject %p", globalObject);
     317    return globalObject;
     318}
     319
     320#endif // USE(JSVALUE64)
     321
    86322} // namespace Integrity
    87323} // namespace JSC
  • 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
  • trunk/Source/JavaScriptCore/tools/IntegrityInlines.h

    r293746 r294017  
    11/*
    2  * Copyright (C) 2019-2020 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
     
    2727
    2828#include "Integrity.h"
     29#include "JSCJSValue.h"
     30#include "StructureID.h"
    2931#include "VM.h"
     32#include "VMInspector.h"
     33#include <wtf/Atomics.h>
     34#include <wtf/Gigacage.h>
    3035
    3136namespace JSC {
     
    6267}
    6368
     69template<AuditLevel auditLevel>
     70ALWAYS_INLINE void auditCell(VM& vm, JSValue value)
     71{
     72    if constexpr (auditLevel == AuditLevel::None)
     73        return;
     74
     75    if (value.isCell())
     76        auditCell<auditLevel>(vm, value.asCell());
     77}
     78
    6479ALWAYS_INLINE void auditCellMinimally(VM& vm, JSCell* cell)
    6580{
     
    7489}
    7590
     91ALWAYS_INLINE void auditCellFully(VM& vm, JSCell* cell)
     92{
     93#if USE(JSVALUE64)
     94    doAudit(vm, cell);
     95#else
     96    auditCellMinimally(vm, cell);
     97#endif
     98}
    7699
    77100ALWAYS_INLINE void auditStructureID(StructureID structureID)
     
    81104    ASSERT(static_cast<uintptr_t>(structureID.bits()) <= structureHeapAddressSize + StructureID::nukedStructureIDBit);
    82105#endif
     106#if ENABLE(EXTRA_INTEGRITY_CHECKS) || ASSERT_ENABLED
     107    Structure* structure = structureID.tryDecode();
     108    IA_ASSERT(structure, "structureID.bits 0x%x", structureID.bits());
     109    // structure should be pointing to readable memory. Force a read.
     110    WTF::opaque(*bitwise_cast<uintptr_t*>(structure));
     111#endif
    83112}
     113
     114#if USE(JSVALUE64)
     115
     116JS_EXPORT_PRIVATE VM* doAuditSlow(VM*);
     117
     118ALWAYS_INLINE VM* doAudit(VM* vm)
     119{
     120    if (UNLIKELY(!VMInspector::isValidVM(vm)))
     121        return doAuditSlow(vm);
     122    return vm;
     123}
     124
     125#endif // USE(JSVALUE64)
    84126
    85127} // namespace Integrity
  • trunk/Source/JavaScriptCore/tools/VMInspector.cpp

    r293779 r294017  
    3939namespace JSC {
    4040
     41VM* VMInspector::m_recentVM { nullptr };
     42
    4143VMInspector& VMInspector::instance()
    4244{
     
    5254{
    5355    Locker locker { m_lock };
     56    m_recentVM = vm;
    5457    m_vmList.append(vm);
    5558}
     
    5861{
    5962    Locker locker { m_lock };
     63    if (m_recentVM == vm)
     64        m_recentVM = nullptr;
    6065    m_vmList.remove(vm);
    6166}
    6267
    6368#if ENABLE(JIT)
    64 static bool ensureIsSafeToLock(Lock& lock)
     69static bool ensureIsSafeToLock(Lock& lock) WTF_IGNORES_THREAD_SAFETY_ANALYSIS
    6570{
    6671    static constexpr unsigned maxRetries = 2;
    6772    unsigned tryCount = 0;
    6873    while (tryCount++ <= maxRetries) {
    69         if (lock.tryLock()) {
    70             lock.unlock();
     74        if (lock.tryLock())
    7175            return true;
    72         }
    7376    }
    7477    return false;
    7578}
    7679#endif // ENABLE(JIT)
     80
     81bool VMInspector::isValidVMSlow(VM* vm)
     82{
     83    bool found = false;
     84    forEachVM([&] (VM& nextVM) {
     85        if (vm == &nextVM) {
     86            m_recentVM = vm;
     87            found = true;
     88            return IterationStatus::Done;
     89        }
     90        return IterationStatus::Continue;
     91    });
     92    return found;
     93}
     94
     95void VMInspector::dumpVMs()
     96{
     97    unsigned i = 0;
     98    WTFLogAlways("Registered VMs:");
     99    forEachVM([&] (VM& nextVM) {
     100        WTFLogAlways("  [%u] VM %p", i++, &nextVM);
     101        return IterationStatus::Continue;
     102    });
     103}
    77104
    78105void VMInspector::forEachVM(Function<IterationStatus(VM&)>&& func)
     
    83110}
    84111
    85 auto VMInspector::isValidExecutableMemory(void* machinePC) -> Expected<bool, Error>
     112WTF_IGNORES_THREAD_SAFETY_ANALYSIS auto VMInspector::isValidExecutableMemory(void* machinePC) -> Expected<bool, Error>
    86113{
    87114#if ENABLE(JIT)
    88     bool found = false;
    89     bool hasTimeout = false;
    90     iterate([&] (VM&) -> IterationStatus {
    91         auto& allocator = ExecutableAllocator::singleton();
    92         auto& lock = allocator.getLock();
    93 
    94         bool isSafeToLock = ensureIsSafeToLock(lock);
    95         if (!isSafeToLock) {
    96             hasTimeout = true;
    97             return IterationStatus::Continue; // Skip this VM.
    98         }
    99 
    100         Locker executableAllocatorLocker { lock };
    101         if (allocator.isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
    102             found = true;
    103             return IterationStatus::Done;
    104         }
    105         return IterationStatus::Continue;
    106     });
    107 
    108     if (!found && hasTimeout)
     115    auto& allocator = ExecutableAllocator::singleton();
     116    auto& lock = allocator.getLock();
     117
     118    bool isSafeToLock = ensureIsSafeToLock(lock);
     119    if (!isSafeToLock)
    109120        return makeUnexpected(Error::TimedOut);
    110     return found;
     121
     122    Locker executableAllocatorLocker { AdoptLock, lock };
     123    if (allocator.isValidExecutableMemory(executableAllocatorLocker, machinePC))
     124        return true;
     125
     126    return false;
    111127#else
    112128    UNUSED_PARAM(machinePC);
     
    120136    CodeBlock* codeBlock = nullptr;
    121137    bool hasTimeout = false;
    122     iterate([&] (VM& vm) {
     138    iterate([&] (VM& vm) WTF_IGNORES_THREAD_SAFETY_ANALYSIS {
     139        if (!vm.isInService())
     140            return IterationStatus::Continue;
     141
    123142        if (!vm.currentThreadIsHoldingAPILock())
    124143            return IterationStatus::Continue;
     
    142161        }
    143162
    144         Locker locker { codeBlockSetLock };
     163        Locker locker { AdoptLock, codeBlockSetLock };
    145164        vm.heap.forEachCodeBlockIgnoringJITPlans(locker, [&] (CodeBlock* cb) {
    146165            JITCode* jitCode = cb->jitCode().get();
  • trunk/Source/JavaScriptCore/tools/VMInspector.h

    r293779 r294017  
    4949    void add(VM*);
    5050    void remove(VM*);
     51    ALWAYS_INLINE static bool isValidVM(VM* vm)
     52    {
     53        return vm == m_recentVM ? true : isValidVMSlow(vm);
     54    }
    5155
    5256    Lock& getLock() WTF_RETURNS_LOCK(m_lock) { return m_lock; }
     
    6266
    6367    JS_EXPORT_PRIVATE static void forEachVM(Function<IterationStatus(VM&)>&&);
     68    JS_EXPORT_PRIVATE static void dumpVMs();
    6469
    6570    Expected<bool, Error> isValidExecutableMemory(void*) WTF_REQUIRES_LOCK(m_lock);
     
    8186    JS_EXPORT_PRIVATE static void dumpSubspaceHashes(VM*);
    8287
    83     enum VerifierAction { ReleaseAssert, Custom };
    84 
    85     using VerifyFunctor = bool(bool condition, const char* description, ...);
    86     static bool unusedVerifier(bool, const char*, ...) { return false; }
    87 
    88     template<VerifierAction, VerifyFunctor = unusedVerifier>
    89     static bool verifyCellSize(JSCell*, size_t allocatorCellSize);
    90 
    91     template<VerifierAction, VerifyFunctor = unusedVerifier>
     88#if USE(JSVALUE64)
    9289    static bool verifyCell(VM&, JSCell*);
     90#endif
    9391
    9492private:
     93    JS_EXPORT_PRIVATE static bool isValidVMSlow(VM*);
     94
    9595    Lock m_lock;
    9696    DoublyLinkedList<VM> m_vmList WTF_GUARDED_BY_LOCK(m_lock);
     97    JS_EXPORT_PRIVATE static VM* m_recentVM;
    9798};
    9899
  • trunk/Source/JavaScriptCore/tools/VMInspectorInlines.h

    r292929 r294017  
    11/*
    2  * Copyright (C) 2019 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 "CellSize.h"
     28#include "Integrity.h"
    2929#include "VMInspector.h"
    30 #include <wtf/Assertions.h>
    3130
    3231namespace JSC {
    3332
    34 #define AUDIT_CONDITION(x) (x), #x
    35 #define AUDIT_VERIFY(action, verifier, cond, ...) do { \
    36         if (action == VerifierAction::ReleaseAssert) \
    37             RELEASE_ASSERT(cond, __VA_ARGS__); \
    38         else if (!verifier(AUDIT_CONDITION(cond), __VA_ARGS__)) \
    39             return false; \
    40     } while (false)
    41 
    42 template<VMInspector::VerifierAction action, VMInspector::VerifyFunctor verifier>
    43 bool VMInspector::verifyCellSize(JSCell* cell, size_t allocatorCellSize)
     33#if USE(JSVALUE64)
     34ALWAYS_INLINE bool VMInspector::verifyCell(VM& vm, JSCell* cell)
    4435{
    45     Structure* structure = cell->structure();
    46     const ClassInfo* classInfo = structure->classInfoForCells();
    47     JSType cellType = cell->type();
    48     AUDIT_VERIFY(action, verifier, cellType == structure->m_blob.type(), cell, cellType, structure->m_blob.type());
    49 
    50     size_t size = cellSize(cell);
    51     AUDIT_VERIFY(action, verifier, size <= allocatorCellSize, cell, cellType, size, allocatorCellSize, classInfo->staticClassSize);
    52     if (isDynamicallySizedType(cellType))
    53         AUDIT_VERIFY(action, verifier, size >= classInfo->staticClassSize, cell, cellType, size, classInfo->staticClassSize);
    54 
    55     return true;
     36    return Integrity::verifyCell(vm, cell);
    5637}
    57 
    58 template<VMInspector::VerifierAction action, VMInspector::VerifyFunctor verifier>
    59 bool VMInspector::verifyCell(VM& vm, JSCell* cell)
    60 {
    61     size_t allocatorCellSize = 0;
    62     if (cell->isPreciseAllocation()) {
    63         PreciseAllocation& preciseAllocation = cell->preciseAllocation();
    64         AUDIT_VERIFY(action, verifier, &preciseAllocation.vm() == &vm, cell, cell->type(), &preciseAllocation.vm(), &vm);
    65 
    66         bool isValidPreciseAllocation = false;
    67         for (auto* i : vm.heap.objectSpace().preciseAllocations()) {
    68             if (i == &preciseAllocation) {
    69                 isValidPreciseAllocation = true;
    70                 break;
    71             }
    72         }
    73         AUDIT_VERIFY(action, verifier, isValidPreciseAllocation, cell, cell->type());
    74 
    75         allocatorCellSize = preciseAllocation.cellSize();
    76     } else {
    77         MarkedBlock& block = cell->markedBlock();
    78         MarkedBlock::Handle& blockHandle = block.handle();
    79         AUDIT_VERIFY(action, verifier, &block.vm() == &vm, cell, cell->type(), &block.vm(), &vm);
    80 
    81         uintptr_t blockStartAddress = reinterpret_cast<uintptr_t>(blockHandle.start());
    82         AUDIT_VERIFY(action, verifier, blockHandle.contains(cell), cell, cell->type(), blockStartAddress, blockHandle.end());
    83 
    84         uintptr_t cellAddress = reinterpret_cast<uintptr_t>(cell);
    85         uintptr_t cellOffset = cellAddress - blockStartAddress;
    86         allocatorCellSize = block.cellSize();
    87         bool cellIsProperlyAligned = !(cellOffset % allocatorCellSize);
    88         AUDIT_VERIFY(action, verifier, cellIsProperlyAligned, cell, cell->type(), allocatorCellSize);
    89     }
    90 
    91     auto cellType = cell->type();
    92     if (cell->type() != JSImmutableButterflyType)
    93         AUDIT_VERIFY(action, verifier, !Gigacage::contains(cell), cell, cellType);
    94 
    95     if (!verifyCellSize<action, verifier>(cell, allocatorCellSize))
    96         return false;
    97 
    98     if (Gigacage::isEnabled(Gigacage::JSValue) && cell->isObject()) {
    99         JSObject* object = asObject(cell);
    100         const Butterfly* butterfly = object->butterfly();
    101         AUDIT_VERIFY(action, verifier, !butterfly || Gigacage::isCaged(Gigacage::JSValue, butterfly), cell, cell->type(), butterfly);
    102     }
    103 
    104     return true;
    105 }
    106 
    107 #undef AUDIT_VERIFY
    108 #undef AUDIT_CONDITION
     38#endif
    10939
    11040} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.