tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WasmFeatures.cpp (11705B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 *
      4 * Copyright 2016 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 #include "wasm/WasmFeatures.h"
     20 
     21 #include "jit/AtomicOperations.h"
     22 #include "jit/JitContext.h"
     23 #include "jit/JitOptions.h"
     24 #include "js/Prefs.h"
     25 #include "util/StringBuilder.h"
     26 #include "vm/JSContext.h"
     27 #include "vm/Realm.h"
     28 #include "vm/StringType.h"
     29 #include "wasm/WasmBaselineCompile.h"
     30 #include "wasm/WasmIonCompile.h"
     31 #include "wasm/WasmSignalHandlers.h"
     32 
     33 using namespace js;
     34 using namespace js::wasm;
     35 using namespace js::jit;
     36 
     37 // About the fuzzer intercession points: If fuzzing has been selected and only a
     38 // single compiler has been selected then we will disable features that are not
     39 // supported by that single compiler.  This is strictly a concession to the
     40 // fuzzer infrastructure.
     41 
     42 static inline bool IsFuzzingIon(JSContext* cx) {
     43  return IsFuzzing() && !cx->options().wasmBaseline() &&
     44         cx->options().wasmIon();
     45 }
     46 
     47 // These functions read flags and apply fuzzing intercession policies.  Never go
     48 // directly to the flags in code below, always go via these accessors.
     49 
     50 static inline bool WasmThreadsFlag(JSContext* cx) {
     51  return cx->realm() &&
     52         cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
     53 }
     54 
     55 #define WASM_FEATURE(NAME, ...) \
     56  static inline bool Wasm##NAME##Flag(JSContext* cx);
     57 JS_FOR_WASM_FEATURES(WASM_FEATURE);
     58 #undef WASM_FEATURE
     59 
     60 #define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \
     61                     FLAG_FORCE_ON, FLAG_FUZZ_ON, PREF)                        \
     62  static inline bool Wasm##NAME##Flag(JSContext* cx) {                         \
     63    if (!(COMPILE_PRED)) {                                                     \
     64      return false;                                                            \
     65    }                                                                          \
     66    return ((FLAG_PRED) && JS::Prefs::wasm_##PREF()) || (FLAG_FORCE_ON);       \
     67  }
     68 JS_FOR_WASM_FEATURES(WASM_FEATURE);
     69 #undef WASM_FEATURE
     70 
     71 static inline bool WasmDebuggerActive(JSContext* cx) {
     72  if (IsFuzzingIon(cx)) {
     73    return false;
     74  }
     75  return cx->realm() && cx->realm()->debuggerObservesWasm();
     76 }
     77 
     78 /*
     79 * [SMDOC] Compiler and feature selection; compiler and feature availability.
     80 *
     81 * In order to make the computation of whether a wasm feature or wasm compiler
     82 * is available predictable, we have established some rules, and implemented
     83 * those rules.
     84 *
     85 * Code elsewhere should use the predicates below to test for features and
     86 * compilers, it should never try to compute feature and compiler availability
     87 * in other ways.
     88 *
     89 * At the outset, there is a set of selected compilers C containing at most one
     90 * baseline compiler [*] and at most one optimizing compiler [**], and a set of
     91 * selected features F.  These selections come from defaults and from overrides
     92 * by command line switches in the shell and javascript.option.wasm_X in the
     93 * browser.  Defaults for both features and compilers may be platform specific,
     94 * for example, some compilers may not be available on some platforms because
     95 * they do not support the architecture at all or they do not support features
     96 * that must be enabled by default on the platform.
     97 *
     98 * [*] Currently we have only one, "baseline" aka "Rabaldr", but other
     99 *     implementations have additional baseline translators, eg from wasm
    100 *     bytecode to an internal code processed by an interpreter.
    101 *
    102 * [**] Currently we have only one, "ion" aka "Baldr".
    103 *
    104 *
    105 * Compiler availability:
    106 *
    107 * The set of features F induces a set of available compilers A: these are the
    108 * compilers that all support all the features in F.  (Some of these compilers
    109 * may not be in the set C.)
    110 *
    111 * The sets C and A are intersected, yielding a set of enabled compilers E.
    112 * Notably, the set E may be empty, in which case wasm is effectively disabled
    113 * (though the WebAssembly object is still present in the global environment).
    114 *
    115 * An important consequence is that selecting a feature that is not supported by
    116 * a particular compiler disables that compiler completely -- there is no notion
    117 * of a compiler being available but suddenly failing when an unsupported
    118 * feature is used by a program.  If a compiler is available, it supports all
    119 * the features that have been selected.
    120 *
    121 * Equally important, a feature cannot be enabled by default on a platform if
    122 * the feature is not supported by all the compilers we wish to have enabled by
    123 * default on the platform.  We MUST by-default disable features on a platform
    124 * that are not supported by all the compilers on the platform.
    125 *
    126 * In a shell build, the testing functions wasmCompilersPresent,
    127 * wasmCompileMode, and wasmIonDisabledByFeatures can be used to probe compiler
    128 * availability and the reasons for a compiler being unavailable.
    129 *
    130 *
    131 * Feature availability:
    132 *
    133 * A feature is available if it is selected and there is at least one available
    134 * compiler that implements it.
    135 *
    136 * For example, --wasm-gc selects the GC feature, and if Baseline is available
    137 * then the feature is available.
    138 *
    139 * In a shell build, there are per-feature testing functions (of the form
    140 * wasmFeatureEnabled) to probe whether specific features are available.
    141 */
    142 
    143 // Compiler availability predicates.  These must be kept in sync with the
    144 // feature predicates in the next section below.
    145 //
    146 // These can't call the feature predicates since the feature predicates call
    147 // back to these predicates.  So there will be a small amount of duplicated
    148 // logic here, but as compilers reach feature parity that duplication will go
    149 // away.
    150 
    151 bool wasm::BaselineAvailable(JSContext* cx) {
    152  if (!cx->options().wasmBaseline() || !BaselinePlatformSupport()) {
    153    return false;
    154  }
    155  bool isDisabled = false;
    156  MOZ_ALWAYS_TRUE(BaselineDisabledByFeatures(cx, &isDisabled));
    157  return !isDisabled;
    158 }
    159 
    160 bool wasm::IonAvailable(JSContext* cx) {
    161  if (!cx->options().wasmIon() || !IonPlatformSupport()) {
    162    return false;
    163  }
    164  bool isDisabled = false;
    165  MOZ_ALWAYS_TRUE(IonDisabledByFeatures(cx, &isDisabled));
    166  return !isDisabled;
    167 }
    168 bool wasm::WasmCompilerForAsmJSAvailable(JSContext* cx) {
    169  return IonAvailable(cx);
    170 }
    171 
    172 template <size_t ArrayLength>
    173 static inline bool Append(JSStringBuilder* reason, const char (&s)[ArrayLength],
    174                          char* sep) {
    175  if ((*sep && !reason->append(*sep)) || !reason->append(s)) {
    176    return false;
    177  }
    178  *sep = ',';
    179  return true;
    180 }
    181 
    182 bool wasm::BaselineDisabledByFeatures(JSContext* cx, bool* isDisabled,
    183                                      JSStringBuilder* reason) {
    184  // Baseline cannot be used if we are testing serialization.
    185  bool testSerialization = WasmTestSerializationFlag(cx);
    186  if (reason) {
    187    char sep = 0;
    188    if (testSerialization && !Append(reason, "testSerialization", &sep)) {
    189      return false;
    190    }
    191  }
    192  *isDisabled = testSerialization;
    193  return true;
    194 }
    195 
    196 bool wasm::IonDisabledByFeatures(JSContext* cx, bool* isDisabled,
    197                                 JSStringBuilder* reason) {
    198  // Ion has no debugging support.
    199  bool debug = WasmDebuggerActive(cx);
    200  bool customPageSizes = WasmCustomPageSizesFlag(cx);
    201  if (reason) {
    202    char sep = 0;
    203    if (debug && !Append(reason, "debug", &sep)) {
    204      return false;
    205    }
    206    if (customPageSizes && !Append(reason, "custom-page-sizes", &sep)) {
    207      return false;
    208    }
    209  }
    210  *isDisabled = debug || customPageSizes;
    211  return true;
    212 }
    213 
    214 bool wasm::AnyCompilerAvailable(JSContext* cx) {
    215  return wasm::BaselineAvailable(cx) || wasm::IonAvailable(cx);
    216 }
    217 
    218 // Feature predicates.  These must be kept in sync with the predicates in the
    219 // section above.
    220 //
    221 // The meaning of these predicates is tricky: A predicate is true for a feature
    222 // if the feature is enabled and/or compiled-in *and* we have *at least one*
    223 // compiler that can support the feature.  Subsequent compiler selection must
    224 // ensure that only compilers that actually support the feature are used.
    225 
    226 #define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, ...) \
    227  bool wasm::NAME##Available(JSContext* cx) {                            \
    228    return Wasm##NAME##Flag(cx) && (COMPILER_PRED);                      \
    229  }
    230 JS_FOR_WASM_FEATURES(WASM_FEATURE)
    231 #undef WASM_FEATURE
    232 
    233 bool wasm::IsPrivilegedContext(JSContext* cx) {
    234  // This may be slightly more lenient than we want in an ideal world, but it
    235  // remains safe.
    236  return cx->realm() && cx->realm()->principals() &&
    237         cx->realm()->principals()->isSystemOrAddonPrincipal();
    238 }
    239 
    240 bool wasm::SimdAvailable(JSContext* cx) {
    241  return js::jit::JitSupportsWasmSimd();
    242 }
    243 
    244 bool wasm::ThreadsAvailable(JSContext* cx) {
    245  return WasmThreadsFlag(cx) && AnyCompilerAvailable(cx);
    246 }
    247 
    248 bool wasm::HasPlatformSupport() {
    249 #if !MOZ_LITTLE_ENDIAN()
    250  return false;
    251 #else
    252 
    253  if (!HasJitBackend()) {
    254    return false;
    255  }
    256 
    257  if (gc::SystemPageSize() > wasm::StandardPageSizeBytes) {
    258    return false;
    259  }
    260 
    261  if (!JitOptions.supportsUnalignedAccesses) {
    262    return false;
    263  }
    264 
    265  if (!jit::JitSupportsAtomics()) {
    266    return false;
    267  }
    268 
    269  // Wasm threads require 8-byte lock-free atomics.
    270  if (!jit::AtomicOperations::isLockfree8()) {
    271    return false;
    272  }
    273 
    274  // Test only whether the compilers are supported on the hardware, not whether
    275  // they are enabled.
    276  return BaselinePlatformSupport() || IonPlatformSupport();
    277 #endif
    278 }
    279 
    280 bool wasm::HasSupport(JSContext* cx) {
    281  // If the general wasm pref is on, it's on for everything.
    282  bool prefEnabled = cx->options().wasm();
    283  // If the general pref is off, check trusted principals.
    284  if (MOZ_UNLIKELY(!prefEnabled)) {
    285    prefEnabled = cx->options().wasmForTrustedPrinciples() && cx->realm() &&
    286                  cx->realm()->principals() &&
    287                  cx->realm()->principals()->isSystemOrAddonPrincipal();
    288  }
    289  // Do not check for compiler availability, as that may be run-time variant.
    290  // For HasSupport() we want a stable answer depending only on prefs.
    291  return prefEnabled && HasPlatformSupport() && EnsureFullSignalHandlers(cx);
    292 }
    293 
    294 bool wasm::StreamingCompilationAvailable(JSContext* cx) {
    295  // This should match EnsureStreamSupport().
    296  return HasSupport(cx) && AnyCompilerAvailable(cx) &&
    297         cx->runtime()->offThreadPromiseState.ref().initialized() &&
    298         CanUseExtraThreads() && cx->runtime()->consumeStreamCallback &&
    299         cx->runtime()->reportStreamErrorCallback;
    300 }
    301 
    302 bool wasm::CodeCachingAvailable(JSContext* cx) {
    303  // Fuzzilli breaks the out-of-process compilation mechanism,
    304  // so we disable it permanently in those builds.
    305 #ifdef FUZZING_JS_FUZZILLI
    306  return false;
    307 #else
    308 
    309  // TODO(bug 1913109): lazy tiering doesn't support serialization
    310  if (JS::Prefs::wasm_lazy_tiering() || JS::Prefs::wasm_lazy_tiering_for_gc()) {
    311    return false;
    312  }
    313 
    314  // At the moment, we require Ion support for code caching.  The main reason
    315  // for this is that wasm::CompileAndSerialize() does not have access to
    316  // information about which optimizing compiler it should use.  See comments in
    317  // CompileAndSerialize(), below.
    318  return StreamingCompilationAvailable(cx) && IonAvailable(cx);
    319 #endif
    320 }