diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 2734e8682108..3eedf8f13d8f 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2007,8 +2007,8 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR for (unsigned int i = 0; i < prefsLen; i++) { const char* prefName = ContentPrefs::GetEarlyPref(i); - MOZ_ASSERT_IF(i > 0, - strcmp(prefName, ContentPrefs::GetEarlyPref(i - 1)) > 0); + MOZ_ASSERT(i == 0 || strcmp(prefName, ContentPrefs::GetEarlyPref(i - 1)) > 0, + "Content process preferences should be sorted alphabetically."); if (!Preferences::MustSendToContentProcesses(prefName)) { continue; diff --git a/dom/ipc/ContentPrefs.cpp b/dom/ipc/ContentPrefs.cpp index 3ffc65555cf2..3007829090ae 100644 --- a/dom/ipc/ContentPrefs.cpp +++ b/dom/ipc/ContentPrefs.cpp @@ -129,6 +129,7 @@ const char* mozilla::dom::ContentPrefs::gEarlyPrefs[] = { "javascript.options.parallel_parsing", "javascript.options.shared_memory", "javascript.options.spectre.index_masking", + "javascript.options.spectre.jit_to_C++_calls", "javascript.options.spectre.object_mitigations.barriers", "javascript.options.spectre.string_mitigations", "javascript.options.spectre.value_masking", diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 1bc99aa04bc8..e3ecc40f2fab 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4305,6 +4305,14 @@ CodeGenerator::visitCallNative(LCallNative* call) // Load the outparam vp[0] into output register(s). masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), JSReturnOperand); + // Until C++ code is instrumented against Spectre, prevent speculative + // execution from returning any private data. + if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() && + call->mir()->hasLiveDefUses()) + { + masm.speculationBarrier(); + } + // The next instruction is removing the footer of the exit frame, so there // is no need for leaveFakeExitFrame. @@ -4443,6 +4451,11 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call) JSReturnOperand); } + // Until C++ code is instrumented against Spectre, prevent speculative + // execution from returning any private data. + if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) + masm.speculationBarrier(); + // The next instruction is removing the footer of the exit frame, so there // is no need for leaveFakeExitFrame. @@ -11993,6 +12006,12 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) masm.loadValue(Address(masm.getStackPointer(), IonDOMExitFrameLayout::offsetOfResult()), JSReturnOperand); } + + // Until C++ code is instrumented against Spectre, prevent speculative + // execution from returning any private data. + if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) + masm.speculationBarrier(); + masm.adjustStack(IonDOMExitFrameLayout::Size()); masm.bind(&haveValue); diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index 15892d4fae2e..b16deb87e925 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -238,6 +238,7 @@ DefaultJitOptions::DefaultJitOptions() SET_DEFAULT(spectreObjectMitigationsBarriers, true); SET_DEFAULT(spectreStringMitigations, true); SET_DEFAULT(spectreValueMasking, true); + SET_DEFAULT(spectreJitToCxxCalls, true); // Toggles whether unboxed plain objects can be created by the VM. SET_DEFAULT(disableUnboxedObjects, false); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 43eadf2208fd..689e8a5ea8e6 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -102,6 +102,7 @@ struct DefaultJitOptions bool spectreObjectMitigationsBarriers; bool spectreStringMitigations; bool spectreValueMasking; + bool spectreJitToCxxCalls; // The options below affect the rest of the VM, and not just the JIT. bool disableUnboxedObjects; diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 48cbde7ea5e9..76cac1db4982 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -3656,6 +3656,44 @@ MacroAssembler::emitPreBarrierFastPath(JSRuntime* rt, MIRType type, Register tem branchTestPtr(Assembler::NonZero, temp2, temp1, noBarrier); } +// ======================================================================== +// Spectre Mitigations. + +void +MacroAssembler::spectreMaskIndex(Register index, Register length, Register output) +{ + MOZ_ASSERT(JitOptions.spectreIndexMasking); + MOZ_ASSERT(length != output); + MOZ_ASSERT(index != output); + + move32(Imm32(0), output); + cmp32Move32(Assembler::Below, index, length, index, output); +} + +void +MacroAssembler::spectreMaskIndex(Register index, const Address& length, Register output) +{ + MOZ_ASSERT(JitOptions.spectreIndexMasking); + MOZ_ASSERT(index != length.base); + MOZ_ASSERT(length.base != output); + MOZ_ASSERT(index != output); + + move32(Imm32(0), output); + cmp32Move32(Assembler::Below, index, length, index, output); +} + +void +MacroAssembler::boundsCheck32PowerOfTwo(Register index, uint32_t length, Label* failure) +{ + MOZ_ASSERT(mozilla::IsPowerOfTwo(length)); + branch32(Assembler::AboveOrEqual, index, Imm32(length), failure); + + // Note: it's fine to clobber the input register, as this is a no-op: it + // only affects speculative execution. + if (JitOptions.spectreIndexMasking) + and32(Imm32(length - 1), index); +} + //}}} check_macroassembler_style void @@ -3707,41 +3745,6 @@ MacroAssembler::debugAssertObjHasFixedSlots(Register obj, Register scratch) #endif } -void -MacroAssembler::spectreMaskIndex(Register index, Register length, Register output) -{ - MOZ_ASSERT(JitOptions.spectreIndexMasking); - MOZ_ASSERT(length != output); - MOZ_ASSERT(index != output); - - move32(Imm32(0), output); - cmp32Move32(Assembler::Below, index, length, index, output); -} - -void -MacroAssembler::spectreMaskIndex(Register index, const Address& length, Register output) -{ - MOZ_ASSERT(JitOptions.spectreIndexMasking); - MOZ_ASSERT(index != length.base); - MOZ_ASSERT(length.base != output); - MOZ_ASSERT(index != output); - - move32(Imm32(0), output); - cmp32Move32(Assembler::Below, index, length, index, output); -} - -void -MacroAssembler::boundsCheck32PowerOfTwo(Register index, uint32_t length, Label* failure) -{ - MOZ_ASSERT(mozilla::IsPowerOfTwo(length)); - branch32(Assembler::AboveOrEqual, index, Imm32(length), failure); - - // Note: it's fine to clobber the input register, as this is a no-op: it - // only affects speculative execution. - if (JitOptions.spectreIndexMasking) - and32(Imm32(length - 1), index); -} - template static bool AddPendingReadBarrier(Vector& list, T* value) diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 752a74aea593..9e05a78ebfe5 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1957,6 +1957,32 @@ class MacroAssembler : public MacroAssemblerSpecific Register offsetTemp, Register maskTemp) DEFINED_ON(mips_shared); + // ======================================================================== + // Spectre Mitigations. + // + // Spectre attacks are side-channel attacks based on cache pollution or + // slow-execution of some instructions. We have multiple spectre mitigations + // possible: + // + // - Stop speculative executions, with memory barriers. Memory barriers + // force all branches depending on loads to be resolved, and thus + // resolve all miss-speculated paths. + // + // - Use conditional move instructions. Some CPUs have a branch predictor, + // and not a flag predictor. In such cases, using a conditional move + // instruction to zero some pointer/index is enough to add a + // data-dependency which prevents any futher executions until the load is + // resolved. + + void spectreMaskIndex(Register index, Register length, Register output); + void spectreMaskIndex(Register index, const Address& length, Register output); + + // The length must be a power of two. Performs a bounds check and Spectre index + // masking. + void boundsCheck32PowerOfTwo(Register index, uint32_t length, Label* failure); + + void speculationBarrier() PER_SHARED_ARCH; + //}}} check_macroassembler_decl_style public: @@ -2141,13 +2167,6 @@ class MacroAssembler : public MacroAssemblerSpecific store32(Imm32(key.constant()), dest); } - void spectreMaskIndex(Register index, Register length, Register output); - void spectreMaskIndex(Register index, const Address& length, Register output); - - // The length must be a power of two. Performs a bounds check and Spectre index - // masking. - void boundsCheck32PowerOfTwo(Register index, uint32_t length, Label* failure); - template void guardedCallPreBarrier(const T& address, MIRType type) { Label done; diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 835402fa2e07..b381dd428260 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -149,6 +149,12 @@ struct VMFunction return returnType; } + // Whether this function returns anything more than a boolean flag for + // failures. + bool returnsData() const { + return returnType == Type_Pointer || outParam != Type_Void; + } + ArgProperties argProperties(uint32_t explicitArg) const { return ArgProperties((argumentProperties >> (2 * explicitArg)) & 3); } diff --git a/js/src/jit/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp index 8b8774ba69e1..4fa80f970c3e 100644 --- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -2163,6 +2163,16 @@ Assembler::as_isb_trap() return writeInst(0xee070f94); } +BufferOffset +Assembler::as_csdb() +{ + // NOP (see as_nop) on architectures where this instruction is not defined. + // + // https://developer.arm.com/-/media/developer/pdf/Cache_Speculation_Side-channels_22Feb18.pdf + // CSDB A32: 1110_0011_0010_0000_1111_0000_0001_0100 + return writeInst(0xe320f000 | 0x14); +} + // Control flow stuff: // bx can *only* branch to a register, never to an immediate. diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 49bdfc999e70..bbc18c400df7 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1606,6 +1606,9 @@ class Assembler : public AssemblerShared BufferOffset as_dmb_trap(); BufferOffset as_isb_trap(); + // Speculation barrier + BufferOffset as_csdb(); + // Control flow stuff: // bx can *only* branch to a register never to an immediate. diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 6f2baec7832c..053e5c0b3174 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -5785,6 +5785,17 @@ MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Regist addDouble(scratchDouble, dest); } +// ======================================================================== +// Spectre Mitigations. + +void +MacroAssembler::speculationBarrier() +{ + // Spectre mitigation recommended by ARM for cases where csel/cmov cannot be + // used. + as_csdb(); +} + //}}} check_macroassembler_style void diff --git a/js/src/jit/arm/Simulator-arm.cpp b/js/src/jit/arm/Simulator-arm.cpp index 0ac901f0ef84..810c582e027e 100644 --- a/js/src/jit/arm/Simulator-arm.cpp +++ b/js/src/jit/arm/Simulator-arm.cpp @@ -282,6 +282,9 @@ class SimInstruction { // Test for a nop instruction, which falls under type 1. inline bool isNopType1() const { return bits(24, 0) == 0x0120F000; } + // Test for a nop instruction, which falls under type 1. + inline bool isCsdbType1() const { return bits(24, 0) == 0x0120F014; } + // Test for a stop instruction. inline bool isStop() const { return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode; @@ -3387,6 +3390,8 @@ Simulator::decodeType01(SimInstruction* instr) } } else if ((type == 1) && instr->isNopType1()) { // NOP. + } else if ((type == 1) && instr->isCsdbType1()) { + // Speculation barrier. (No-op for the simulator) } else { int rd = instr->rdValue(); int rn = instr->rnValue(); diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index f121cf3d95c7..7f6062d5275a 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -878,6 +878,12 @@ JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunct MOZ_ASSERT(f.outParam == Type_Void); break; } + + // Until C++ code is instrumented against Spectre, prevent speculative + // execution from returning any private data. + if (f.returnsData() && JitOptions.spectreJitToCxxCalls) + masm.speculationBarrier(); + masm.leaveExitFrame(); masm.retn(Imm32(sizeof(ExitFrameLayout) + f.explicitStackSlots() * sizeof(void*) + diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index 361b6e0e52fd..5bcce92b3bdd 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -1862,6 +1862,16 @@ MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& atomicEffectOp(arrayType, sync, op, value, mem, temp); } +// ======================================================================== +// Spectre Mitigations. + +void +MacroAssembler::speculationBarrier() +{ + // Conditional speculation barrier. + csdb(); +} + //}}} check_macroassembler_style } // namespace jit diff --git a/js/src/jit/arm64/Trampoline-arm64.cpp b/js/src/jit/arm64/Trampoline-arm64.cpp index e248522a378e..f115bc8acecb 100644 --- a/js/src/jit/arm64/Trampoline-arm64.cpp +++ b/js/src/jit/arm64/Trampoline-arm64.cpp @@ -702,6 +702,11 @@ JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunct break; } + // Until C++ code is instrumented against Spectre, prevent speculative + // execution from returning any private data. + if (f.returnsData() && JitOptions.spectreJitToCxxCalls) + masm.speculationBarrier(); + masm.leaveExitFrame(); masm.retn(Imm32(sizeof(ExitFrameLayout) + f.explicitStackSlots() * sizeof(void*) + diff --git a/js/src/jit/arm64/vixl/Assembler-vixl.h b/js/src/jit/arm64/vixl/Assembler-vixl.h index 21e0c34ba599..d1c414a0d38d 100644 --- a/js/src/jit/arm64/vixl/Assembler-vixl.h +++ b/js/src/jit/arm64/vixl/Assembler-vixl.h @@ -1818,6 +1818,13 @@ class Assembler : public MozBaseAssembler { } static void nop(Instruction* at); + // Alias for system instructions. + // Conditional speculation barrier. + BufferOffset csdb() { + return hint(CSDB); + } + static void csdb(Instruction* at); + // FP and NEON instructions. // Move double precision immediate to FP register. void fmov(const VRegister& vd, double imm); diff --git a/js/src/jit/arm64/vixl/Constants-vixl.h b/js/src/jit/arm64/vixl/Constants-vixl.h index 3724cf4b31ca..9b124126eee3 100644 --- a/js/src/jit/arm64/vixl/Constants-vixl.h +++ b/js/src/jit/arm64/vixl/Constants-vixl.h @@ -321,7 +321,10 @@ enum SystemHint { WFE = 2, WFI = 3, SEV = 4, - SEVL = 5 + SEVL = 5, + // No-op on architectures where this instruction is not defined. + // https://developer.arm.com/-/media/developer/pdf/Cache_Speculation_Side-channels_22Feb18.pdf + CSDB = 0x14 }; enum BarrierDomain { diff --git a/js/src/jit/arm64/vixl/Decoder-vixl.cpp b/js/src/jit/arm64/vixl/Decoder-vixl.cpp index c74f71a11b24..5ebda0fa52d3 100644 --- a/js/src/jit/arm64/vixl/Decoder-vixl.cpp +++ b/js/src/jit/arm64/vixl/Decoder-vixl.cpp @@ -228,7 +228,7 @@ void Decoder::DecodeBranchSystemException(const Instruction* instr) { (instr->Mask(0x003CE000) == 0x00042000) || (instr->Mask(0x003FFFC0) == 0x000320C0) || (instr->Mask(0x003FF100) == 0x00032100) || - (instr->Mask(0x003FF200) == 0x00032200) || + // (instr->Mask(0x003FF200) == 0x00032200) || // match CSDB (instr->Mask(0x003FF400) == 0x00032400) || (instr->Mask(0x003FF800) == 0x00032800) || (instr->Mask(0x0038F000) == 0x00005000) || diff --git a/js/src/jit/arm64/vixl/Disasm-vixl.cpp b/js/src/jit/arm64/vixl/Disasm-vixl.cpp index bf2ad840c08c..2b729f7c1c31 100644 --- a/js/src/jit/arm64/vixl/Disasm-vixl.cpp +++ b/js/src/jit/arm64/vixl/Disasm-vixl.cpp @@ -1321,6 +1321,11 @@ void Disassembler::VisitSystem(const Instruction* instr) { form = NULL; break; } + case CSDB: { + mnemonic = "csdb"; + form = NULL; + break; + } } } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { switch (instr->Mask(MemBarrierMask)) { diff --git a/js/src/jit/arm64/vixl/Instructions-vixl.h b/js/src/jit/arm64/vixl/Instructions-vixl.h index b3664fd19dd5..8990ac56425b 100644 --- a/js/src/jit/arm64/vixl/Instructions-vixl.h +++ b/js/src/jit/arm64/vixl/Instructions-vixl.h @@ -309,6 +309,7 @@ class Instruction { bool IsCBNZ() const; bool IsLDR() const; bool IsNOP() const; + bool IsCSDB() const; bool IsADR() const; bool IsADRP() const; bool IsMovz() const; diff --git a/js/src/jit/arm64/vixl/MacroAssembler-vixl.h b/js/src/jit/arm64/vixl/MacroAssembler-vixl.h index b3735e5ee9f3..f5e94f1714f8 100644 --- a/js/src/jit/arm64/vixl/MacroAssembler-vixl.h +++ b/js/src/jit/arm64/vixl/MacroAssembler-vixl.h @@ -1123,6 +1123,10 @@ class MacroAssembler : public js::jit::Assembler { SingleEmissionCheckScope guard(this); nop(); } + void Csdb() { + SingleEmissionCheckScope guard(this); + csdb(); + } void Rbit(const Register& rd, const Register& rn) { VIXL_ASSERT(!rd.IsZero()); VIXL_ASSERT(!rn.IsZero()); diff --git a/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp b/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp index 6b4c54bcb42d..3317347b818d 100644 --- a/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp @@ -409,6 +409,11 @@ void Assembler::nop(Instruction* at) { } +void Assembler::csdb(Instruction* at) { + hint(at, CSDB); +} + + BufferOffset Assembler::Logical(const Register& rd, const Register& rn, const Operand operand, LogicalOp op) { diff --git a/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp b/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp index 8547341bf8af..ffe0893d7d9c 100644 --- a/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp @@ -85,6 +85,11 @@ bool Instruction::IsNOP() const { } +bool Instruction::IsCSDB() const { + return Mask(SystemHintMask) == HINT && ImmHint() == CSDB; +} + + bool Instruction::IsADR() const { return Mask(PCRelAddressingMask) == ADR; } diff --git a/js/src/jit/arm64/vixl/Simulator-vixl.cpp b/js/src/jit/arm64/vixl/Simulator-vixl.cpp index ea2031e3bc32..d2b9720c1815 100644 --- a/js/src/jit/arm64/vixl/Simulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/Simulator-vixl.cpp @@ -2326,6 +2326,7 @@ void Simulator::VisitSystem(const Instruction* instr) { VIXL_ASSERT(instr->Mask(SystemHintMask) == HINT); switch (instr->ImmHint()) { case NOP: break; + case CSDB: break; default: VIXL_UNIMPLEMENTED(); } } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index 76824d8e4a63..9e5d34eb55cb 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -766,6 +766,12 @@ JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunct MOZ_ASSERT(f.outParam == Type_Void); break; } + + // Until C++ code is instrumented against Spectre, prevent speculative + // execution from returning any private data. + if (f.returnsData() && JitOptions.spectreJitToCxxCalls) + masm.speculationBarrier(); + masm.leaveExitFrame(); masm.retn(Imm32(sizeof(ExitFrameLayout) + f.explicitStackSlots() * sizeof(void*) + diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 167b4e74b2d1..74babb3fc31b 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -3743,9 +3743,13 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off m_formatter.immediate16u(imm); } + void lfence() { + spew("lfence"); + m_formatter.twoByteOp(OP_FENCE, (RegisterID)0, 0b101); + } void mfence() { spew("mfence"); - m_formatter.twoByteOp(OP_FENCE, (RegisterID)0, 6); + m_formatter.twoByteOp(OP_FENCE, (RegisterID)0, 0b110); } // Assembler admin methods: diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp index 9228c9b70790..aa8759301385 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp @@ -1418,4 +1418,16 @@ MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& s AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, temp1, temp2, output); } +// ======================================================================== +// Spectre Mitigations. + +void +MacroAssembler::speculationBarrier() +{ + // Spectre mitigation recommended by Intel and AMD suggest to use lfence as + // a way to force all speculative execution of instructions to end. + MOZ_ASSERT(HasSSE2()); + masm.lfence(); +} + //}}} check_macroassembler_style diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index adc6aa5787c9..9f3252f6fcf9 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -782,6 +782,12 @@ JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunct MOZ_ASSERT(f.outParam == Type_Void); break; } + + // Until C++ code is instrumented against Spectre, prevent speculative + // execution from returning any private data. + if (f.returnsData() && JitOptions.spectreJitToCxxCalls) + masm.speculationBarrier(); + masm.leaveExitFrame(); masm.retn(Imm32(sizeof(ExitFrameLayout) + f.explicitStackSlots() * sizeof(void*) + diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 6e5ac541407c..01b5e9059e41 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -7287,6 +7287,9 @@ JS_SetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t v case JSJITCOMPILER_SPECTRE_VALUE_MASKING: jit::JitOptions.spectreValueMasking = !!value; break; + case JSJITCOMPILER_SPECTRE_JIT_TO_CXX_CALLS: + jit::JitOptions.spectreJitToCxxCalls = !!value; + break; case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE: jit::JitOptions.asmJSAtomicsEnable = !!value; break; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index de0781d377a9..cf1632ca3ef4 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5894,6 +5894,7 @@ JS_SetOffthreadIonCompilationEnabled(JSContext* cx, bool enabled); Register(SPECTRE_OBJECT_MITIGATIONS_BARRIERS, "spectre.object-mitigations.barriers") \ Register(SPECTRE_STRING_MITIGATIONS, "spectre.string-mitigations") \ Register(SPECTRE_VALUE_MASKING, "spectre.value-masking") \ + Register(SPECTRE_JIT_TO_CXX_CALLS, "spectre.jit-to-C++-calls") \ Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \ Register(WASM_FOLD_OFFSETS, "wasm.fold-offsets") \ Register(WASM_DELAY_TIER2, "wasm.delay-tier2") diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index a2baab96dfbb..ef226a55098a 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -8601,11 +8601,13 @@ SetContextOptions(JSContext* cx, const OptionParser& op) jit::JitOptions.spectreObjectMitigationsBarriers = true; jit::JitOptions.spectreStringMitigations = true; jit::JitOptions.spectreValueMasking = true; + jit::JitOptions.spectreJitToCxxCalls = true; } else if (strcmp(str, "off") == 0) { jit::JitOptions.spectreIndexMasking = false; jit::JitOptions.spectreObjectMitigationsBarriers = false; jit::JitOptions.spectreStringMitigations = false; jit::JitOptions.spectreValueMasking = false; + jit::JitOptions.spectreJitToCxxCalls = false; } else { return OptionFailure("spectre-mitigations", str); } diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index ee6403f03b1e..10a9c36d90a2 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -813,6 +813,7 @@ ReloadPrefsCallback(const char* pref, void* data) bool spectreStringMitigations = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.string_mitigations"); bool spectreValueMasking = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.value_masking"); + bool spectreJitToCxxCalls = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.jit_to_C++_calls"); sSharedMemoryEnabled = Preferences::GetBool(JS_OPTIONS_DOT_STR "shared_memory"); @@ -880,6 +881,8 @@ ReloadPrefsCallback(const char* pref, void* data) JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_STRING_MITIGATIONS, spectreStringMitigations); JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_VALUE_MASKING, spectreValueMasking); + JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_JIT_TO_CXX_CALLS, + spectreJitToCxxCalls); } XPCJSContext::~XPCJSContext() diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index a152e4944e95..388c4158423d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1576,6 +1576,7 @@ pref("javascript.options.spectre.index_masking", true); pref("javascript.options.spectre.object_mitigations.barriers", true); pref("javascript.options.spectre.string_mitigations", true); pref("javascript.options.spectre.value_masking", true); +pref("javascript.options.spectre.jit_to_C++_calls", true); // Streams API pref("javascript.options.streams", false);