Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: d5e7d2a3eeeeab55e93553b2fc91fc61327a6ffb
      
https://github.com/WebKit/WebKit/commit/d5e7d2a3eeeeab55e93553b2fc91fc61327a6ffb
  Author: Mark Lam <mark....@apple.com>
  Date:   2025-08-17 (Sun, 17 Aug 2025)

  Changed paths:
    M Source/JavaScriptCore/interpreter/VMEntryRecord.h
    M Source/JavaScriptCore/llint/InPlaceInterpreter.asm
    M Source/JavaScriptCore/llint/InPlaceInterpreter.cpp
    M Source/JavaScriptCore/llint/InPlaceInterpreter.h
    M Source/JavaScriptCore/llint/InPlaceInterpreter64.asm
    M Source/JavaScriptCore/llint/LLIntData.cpp
    M Source/JavaScriptCore/llint/LLIntData.h
    M Source/JavaScriptCore/llint/LowLevelInterpreter.asm
    M Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
    M Source/JavaScriptCore/llint/WebAssembly.asm
    M Source/JavaScriptCore/offlineasm/arm64e.rb
    M Source/JavaScriptCore/runtime/InitializeThreading.cpp
    M Source/JavaScriptCore/runtime/JSCConfig.h
    M Source/JavaScriptCore/runtime/JSCJSValue.cpp
    M Source/JavaScriptCore/runtime/JSCPtrTag.h
    M Source/JavaScriptCore/runtime/ParseInt.h
    M Source/WTF/wtf/WTFConfig.cpp
    M Source/WTF/wtf/WTFConfig.h

  Log Message:
  -----------
  Harden the interpreter dispatchers more for ARM64E.
https://bugs.webkit.org/show_bug.cgi?id=295595
rdar://155356829

Reviewed by Daniel Liu.

We have 3 goals:
1. Ensure that LLInt opcode maps are robust against corruption.
2. Ensure that processes that don't need the LLInt can disable it.
3. Ensure that opcode maps are properly initialized before use.

Goal 1: Ensure that LLInt opcode maps are robust against corruption.
====================================================================
We previously achieve this by PAC signing (address diversified) the handlers 
PCs in
g_opcodeMap (and peers).  The handler PCs are authenticated on each bytecode 
dispatch.
This is slow because PAC authentication is slow.

We now achieve this goal by moving g_opcodeMap (and peers) into a 
g_opcodeConfigStorage
page that can be permanently frozen as Read Only.  With this, the opcode map 
can no longer
be corrupted, and we can do away with the PAC signing.  The handler PCs will 
now be stored
as naked pointers.

Here are the steps we use to initialize this Read Only opcode maps:

 1.1. Call llint_entry to fill in the PAC signed handler PCs into a temp 
buffer.  Next, we
      strip these handler PCs of their PAC signatures and store them as naked 
code pointers
      into the g_opcodeConfigStorage.

 1.2. Permanently freeze (protect) the g_opcodeConfigStorage page with 
PROT_READ permission
      only.

 1.3. For each handler PC in the opcode maps, PAC authenticate their 
counterpart in the
      temp buffer, and then, assert that the naked handler PC matches the 
authenticated
      counterpart.

      This steps guarantees that the opcode maps have been initialized 
properly, and
      are not corrupted in any way.

 1.4. Initialize g_jscConfig.llint.gateMap[Gate::vmEntryToJavaScript] with its 
PAC signed
      pointer.  This pointer is the vector thru which vmEntryToJavaScript calls 
into the
      interpreter (or JIT generated functions). See makeJavaScriptCall().

Goal 2. Ensure that processes that don't need the LLInt can disable it.
=======================================================================
We previously achieve this using a neuterOpcodeMaps() function that fills the 
opcode maps
with the PC of a function that will crash.

We now achieve this by permanently freeze (protect) the g_opcodeConfigStorage 
page with
PROT_NONE.  This ensures that LLInt cannot use the opcode maps.  Any attempt to 
use them
will now result in a crash.

We also do NOT initialize g_jscConfig.llint.gateMap[Gate::vmEntryToJavaScript] 
(nor any
of the other values initialized by LLInt:initialize()).

Also added a check for a "com.apple.security.script-restrictions" entitlement 
that can be
used by processes to opt into disabling the LLInt.  This is not currently used 
by any
processes.

We may change the implementation of this goal in the future if a more 
performant way becomes
available.  See rdar://158509720.

Goal 3. Ensure that opcode maps are properly initialized before use.
====================================================================
"before use" means cases where the process' code will naturally call into the 
LLInt.  What
we're hardening against here are cases where the LLInt hasn't been initialized 
yet, but
its data has been corrupted such that LLInt will mistakenly think that it has 
already been
initialized.

However, all code paths to enter the LLInt will have to dispatch through
g_jscConfig.llint.gateMap[Gate::vmEntryToJavaScript], which will PAC 
authenticate its
pointer.  Hence, the only way to enter the LLInt is if the 
Gate::vmEntryToJavaScript
pointer is properly signed.  And the only way it will be properly signed is if 
we have
already gone through LLInt::initialized(), and properly initialized the opcode 
maps.

Just to ensure that g_jscConfig.llint.gateMap[Gate::vmEntryToJavaScript] is 
uniquely signed,
we introduce a new VMEntryToJITGatePtrTag that is only used exclusively for 
this pointer.

See rdar://155356829 for more details.

We also made these additional changes:

1. Secure IPInt dispatchers
   ========================
   This is achieved by calling validateOpcodeConfig() at all the following 
places:
   a. the top of all IPInt handler for Wasm opcode that concerns control flow.
   b. before any operation calls.
   c. the top of loops in IPInt, unless it the loop is already covered by (a) 
or (b).

   The idea is to only validateOpcodeConfig() at all places that concerns 
control flow.
   The alternative would be do it on every bytecode dispatch, but that turns 
out to be
   too costly in terms of performance (~2-5% overhead on non-JIT IPInt only).

   validateOpcodeConfig() simply loads a word from g_opcodeConfigStorage.  If 
the LLint
   is disabled, g_opcodeConfigStorage will be mapped PROT_NONE, and this load 
will result
   in a crash.  The word loaded by validateOpcodeConfig() is ignored.  No other
   instructions depend on it.  Hence, this should help reduce the impact of 
that load in
   terms of performance.

   We also make the IPInt gc_dispatch, conversion_dispatch, simd_dispatch, and
   atomic_dispatch use base pointers from g_opcodeConfigStorage.  These have 
not been
   shown to impact performance so far.  So, they are fine for now.

   See rdar://155356829 for more details.

2. Moved IPInt initialization into LLInt::initialize() so that it can be done 
at the right
   time before we protect the g_opcodeConfigStorage.  IPInt dispatch base 
pointers are
   now moved from g_config into g_opcodeConfigStorage.

3. Now that we only store naked code pointers in the opcode maps, a lot of the 
LLInt
   bytecode accessor methods can be simplified significantly.  This has been 
done.

4. Move the error handling crash case out of argumINTDispatch(), 
mintArgDispatch(),
   mintRetDispatch(), and uintDispatch().  This makes the code slightly more 
performant.

5. Initially, this set of changes resulted in a RAMification memory regression. 
 To fix
   this, we do the following:

   a. Move the definition of the radixDigits const array from ParseInt.h to 
JSCJSValue.cpp.
      The contents of the array is not used to enable any constant folding.  
So, it doesn't
      help anything to make the array inlineable.  This move reduces the memory 
use of this
      array from ~280K down to 37 bytes.

   b. Put g_config and g_opcodeConfigStorage pages in their own custom __DATA 
sections
      (using the __attribute__ modifier).  Previously, these 2 globals were 
linked in between
      other globals in __DATA.  Due to their page alignment requirements, the 
linker
      padded the space before them with multiple KBs of unused memory.  Putting 
them into
      their own sections allows the linker to pack in other global variables 
more
      efficiently.

   With (a) and (b), the RAMification regression was completely resolved.

7. Added a static_assert to ensure that VMEntryRecord::m_prevTopEntryFrame is 
located
   adjacent to VMEntryRecord::m_prevTopCallFrame.  We have pre-existing code 
that rely
   on this invariant.

This change was shown to be performance neutral on benchmarks with JIT enabled.
With JIT disabled, the IPInt hardening also appears to be performance neutral.

* Source/JavaScriptCore/bytecode/Opcode.h:
* Source/JavaScriptCore/interpreter/VMEntryRecord.h:
* Source/JavaScriptCore/llint/InPlaceInterpreter.asm:
* Source/JavaScriptCore/llint/InPlaceInterpreter.cpp:
(JSC::IPInt::initialize):
(JSC::IPInt::verifyInitialization):
* Source/JavaScriptCore/llint/InPlaceInterpreter.h:
* Source/JavaScriptCore/llint/InPlaceInterpreter64.asm:
* Source/JavaScriptCore/llint/LLIntData.cpp:
(JSC::LLInt::scriptingIsForbidden):
(JSC::LLInt::initialize):
(JSC::LLInt::neuterOpcodeMaps): Deleted.
* Source/JavaScriptCore/llint/LLIntData.h:
(JSC::LLInt::addressOfOpcodeConfig):
(JSC::LLInt::getCodePtrImpl):
(JSC::LLInt::getCodePtr):
(JSC::LLInt::getWide16CodePtr):
(JSC::LLInt::getWide32CodePtr):
(JSC::LLInt::getOpcodeAddress): Deleted.
(JSC::LLInt::getOpcodeWide16Address): Deleted.
(JSC::LLInt::getOpcodeWide32Address): Deleted.
* Source/JavaScriptCore/llint/LowLevelInterpreter.asm:
* Source/JavaScriptCore/llint/LowLevelInterpreter64.asm:
* Source/JavaScriptCore/llint/WebAssembly.asm:
* Source/JavaScriptCore/offlineasm/arm64e.rb:
* Source/JavaScriptCore/runtime/InitializeThreading.cpp:
(JSC::initialize):
* Source/JavaScriptCore/runtime/JSCConfig.h:
* Source/JavaScriptCore/runtime/JSCJSValue.cpp:
* Source/JavaScriptCore/runtime/JSCPtrTag.h:
* Source/JavaScriptCore/runtime/ParseInt.h:
* Source/WTF/wtf/WTFConfig.cpp:
(WTF::Config::permanentlyFreeze):
(WTF::permanentlyFreezePages):
* Source/WTF/wtf/WTFConfig.h:

Canonical link: https://commits.webkit.org/298816@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to