| /* |
| * Copyright (C) 2019-2022 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 <wtf/Assertions.h> |
| #include <wtf/Lock.h> |
| |
| #if OS(DARWIN) |
| #include <mach/vm_param.h> |
| #endif |
| |
| #if USE(JSVALUE32) |
| #define ENABLE_EXTRA_INTEGRITY_CHECKS 0 // Not supported. |
| #else |
| // Force ENABLE_EXTRA_INTEGRITY_CHECKS to 1 for your local build if you want |
| // more prolific audits to be enabled. |
| #define ENABLE_EXTRA_INTEGRITY_CHECKS 0 |
| #endif |
| |
| // From API/JSBase.h |
| typedef const struct OpaqueJSContextGroup* JSContextGroupRef; |
| typedef const struct OpaqueJSContext* JSContextRef; |
| typedef struct OpaqueJSContext* JSGlobalContextRef; |
| typedef struct OpaqueJSPropertyNameAccumulator* JSPropertyNameAccumulatorRef; |
| typedef const struct OpaqueJSValue* JSValueRef; |
| typedef struct OpaqueJSValue* JSObjectRef; |
| |
| namespace WTF { |
| class PrintStream; |
| } |
| |
| namespace JSC { |
| |
| class JSCell; |
| class JSGlobalObject; |
| class JSObject; |
| class JSValue; |
| class Structure; |
| class StructureID; |
| 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 static bool isSanePointer(const void* pointer) |
| { |
| #if CPU(ADDRESS64) |
| uintptr_t pointerAsInt = bitwise_cast<uintptr_t>(pointer); |
| #if OS(DARWIN) |
| constexpr uintptr_t oneAbove4G = (static_cast<uintptr_t>(1) << 32); |
| if (pointerAsInt < oneAbove4G) |
| return false; |
| #endif |
| uintptr_t canonicalPointerBits = pointerAsInt << (64 - OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH)); |
| uintptr_t nonCanonicalPointerBits = pointerAsInt >> OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH); |
| return !nonCanonicalPointerBits && canonicalPointerBits; |
| #else |
| UNUSED_PARAM(pointer); |
| return true; |
| #endif // CPU(ADDRESS64) |
| } |
| |
| #if USE(JSVALUE64) |
| |
| class Analyzer { |
| public: |
| enum Action { LogOnly, LogAndCrash }; |
| |
| static bool analyzeVM(VM&, Action); |
| static bool analyzeCell(VM&, JSCell*, Action); |
| static bool analyzeCell(JSCell*, Action); |
| }; |
| |
| JS_EXPORT_PRIVATE JSContextRef doAudit(JSContextRef); |
| JS_EXPORT_PRIVATE JSGlobalContextRef doAudit(JSGlobalContextRef); |
| JS_EXPORT_PRIVATE JSObjectRef doAudit(JSObjectRef); |
| JS_EXPORT_PRIVATE JSValueRef doAudit(JSValueRef); |
| |
| JS_EXPORT_PRIVATE JSValue doAudit(JSValue); |
| JS_EXPORT_PRIVATE JSCell* doAudit(JSCell*); |
| JS_EXPORT_PRIVATE JSCell* doAudit(VM&, JSCell*); |
| JS_EXPORT_PRIVATE JSObject* doAudit(JSObject*); |
| JS_EXPORT_PRIVATE JSGlobalObject* doAudit(JSGlobalObject*); |
| |
| VM* doAudit(VM*); // see IntegrityInlines.h |
| |
| // These are used for debugging queries, and will not crash. |
| JS_EXPORT_PRIVATE bool verifyCell(JSCell*); |
| JS_EXPORT_PRIVATE bool verifyCell(VM&, JSCell*); |
| |
| #endif // USE(JSVALUE64) |
| |
| ALWAYS_INLINE void auditCellRandomly(VM&, JSCell*); |
| ALWAYS_INLINE void auditCellMinimally(VM&, JSCell*); |
| JS_EXPORT_PRIVATE void auditCellMinimallySlow(VM&, JSCell*); |
| ALWAYS_INLINE 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) |
| { |
| static_assert(auditLevel == AuditLevel::None || auditLevel == AuditLevel::Minimal || auditLevel == AuditLevel::Full || auditLevel == AuditLevel::Random); |
| |
| UNUSED_PARAM(vm); |
| UNUSED_PARAM(cell); |
| if constexpr (auditLevel == AuditLevel::None) |
| return; |
| if constexpr (auditLevel == AuditLevel::Minimal) |
| return auditCellMinimally(vm, cell); |
| if constexpr (auditLevel == AuditLevel::Full) |
| return auditCellFully(vm, cell); |
| if constexpr (auditLevel == AuditLevel::Random) |
| return auditCellRandomly(vm, cell); |
| } |
| |
| template<AuditLevel auditLevel = DefaultAuditLevel> |
| ALWAYS_INLINE void auditCell(VM&, JSValue); |
| |
| ALWAYS_INLINE void auditStructureID(StructureID); |
| |
| #if ENABLE(EXTRA_INTEGRITY_CHECKS) && USE(JSVALUE64) |
| template<typename T> ALWAYS_INLINE T audit(T value) { return doAudit(value); } |
| #else |
| template<typename T> ALWAYS_INLINE T audit(T value) { return value; } |
| #endif |
| |
| #if COMPILER(MSVC) || !VA_OPT_SUPPORTED |
| |
| #define IA_LOG(assertion, format, ...) do { \ |
| Integrity::logLnF("ERROR: %s @ %s:%d", #assertion, __FILE__, __LINE__); \ |
| } while (false) |
| |
| #define IA_ASSERT_WITH_ACTION(assertion, action, ...) do { \ |
| if (UNLIKELY(!(assertion))) { \ |
| IA_LOG(assertion, __VA_ARGS__); \ |
| WTFReportBacktraceWithPrefixAndPrintStream(Integrity::logFile(), " "); \ |
| action; \ |
| } \ |
| } while (false) |
| |
| #define IA_ASSERT(assertion, ...) \ |
| IA_ASSERT_WITH_ACTION(assertion, { \ |
| RELEASE_ASSERT((assertion)); \ |
| }) |
| |
| #else // not (COMPILER(MSVC) || !VA_OPT_SUPPORTED) |
| |
| #define IA_LOG(assertion, format, ...) do { \ |
| Integrity::logLnF("ERROR: %s @ %s:%d", #assertion, __FILE__, __LINE__); \ |
| Integrity::logLnF(" " format __VA_OPT__(,) __VA_ARGS__); \ |
| } while (false) |
| |
| #define IA_ASSERT_WITH_ACTION(assertion, action, ...) do { \ |
| if (UNLIKELY(!(assertion))) { \ |
| IA_LOG(assertion, __VA_ARGS__); \ |
| WTFReportBacktraceWithPrefixAndPrintStream(Integrity::logFile(), " "); \ |
| action; \ |
| } \ |
| } while (false) |
| |
| #define IA_ASSERT(assertion, ...) \ |
| IA_ASSERT_WITH_ACTION(assertion, { \ |
| RELEASE_ASSERT((assertion) __VA_OPT__(,) __VA_ARGS__); \ |
| } __VA_OPT__(,) __VA_ARGS__) |
| |
| #endif // COMPILER(MSVC) || !VA_OPT_SUPPORTED |
| |
| JS_EXPORT_PRIVATE WTF::PrintStream& logFile(); |
| JS_EXPORT_PRIVATE void logF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); |
| JS_EXPORT_PRIVATE void logLnF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); |
| |
| } // namespace Integrity |
| |
| } // namespace JSC |