mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 20:30:41 +00:00
Bug 1433111 - Zero the payload if the Value tag does not match the expected tag. r=jandem
This commit is contained in:
parent
7e7bf4886a
commit
f37e8775a7
@ -311,6 +311,10 @@ CanonicalizeNaN(double d)
|
||||
* that toString, toObject, toSymbol will return an invalid pointer (because
|
||||
* some high bits will be set) when called on a Value with a different type
|
||||
* tag.
|
||||
*
|
||||
* - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a
|
||||
* conditional move (not speculated) to zero the payload register if the type
|
||||
* doesn't match.
|
||||
*/
|
||||
class MOZ_NON_PARAM alignas(8) Value
|
||||
{
|
||||
|
@ -234,6 +234,7 @@ DefaultJitOptions::DefaultJitOptions()
|
||||
|
||||
SET_DEFAULT(spectreIndexMasking, true);
|
||||
SET_DEFAULT(spectreStringMitigations, true);
|
||||
SET_DEFAULT(spectreValueMasking, true);
|
||||
|
||||
// Toggles whether unboxed plain objects can be created by the VM.
|
||||
SET_DEFAULT(disableUnboxedObjects, false);
|
||||
|
@ -94,8 +94,12 @@ struct DefaultJitOptions
|
||||
mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
|
||||
mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
|
||||
|
||||
// Spectre mitigation flags. Each mitigation has its own flag in order to
|
||||
// measure the effectiveness of each mitigation with various proof of
|
||||
// concept.
|
||||
bool spectreIndexMasking;
|
||||
bool spectreStringMitigations;
|
||||
bool spectreValueMasking;
|
||||
|
||||
// The options below affect the rest of the VM, and not just the JIT.
|
||||
bool disableUnboxedObjects;
|
||||
|
@ -1343,6 +1343,9 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void sbbl(Register src, Register dest) {
|
||||
masm.sbbl_rr(src.encoding(), dest.encoding());
|
||||
}
|
||||
void subl(Register src, Register dest) {
|
||||
masm.subl_rr(src.encoding(), dest.encoding());
|
||||
}
|
||||
@ -1630,6 +1633,9 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.andl_mr(src.disp(), src.base(), dest.encoding());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.andl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
@ -2114,6 +2120,9 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.push_m(src.disp(), src.base());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.push_m(src.disp(), src.base(), src.index(), src.scale());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
|
@ -303,6 +303,11 @@ public:
|
||||
spew("push " MEM_ob, ADDR_ob(offset, base));
|
||||
m_formatter.oneByteOp(OP_GROUP5_Ev, offset, base, GROUP5_OP_PUSH);
|
||||
}
|
||||
void push_m(int32_t offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("push " MEM_obs, ADDR_obs(offset, base, index, scale));
|
||||
m_formatter.oneByteOp(OP_GROUP5_Ev, offset, base, index, scale, GROUP5_OP_PUSH);
|
||||
}
|
||||
|
||||
void pop_m(int32_t offset, RegisterID base)
|
||||
{
|
||||
@ -923,6 +928,12 @@ public:
|
||||
m_formatter.oneByteOp(OP_AND_GvEv, offset, base, dst);
|
||||
}
|
||||
|
||||
void andl_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
|
||||
{
|
||||
spew("andl " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg32Name(dst));
|
||||
m_formatter.oneByteOp(OP_AND_GvEv, offset, base, index, scale, dst);
|
||||
}
|
||||
|
||||
void andl_rm(RegisterID src, int32_t offset, RegisterID base)
|
||||
{
|
||||
spew("andl %s, " MEM_ob, GPReg32Name(src), ADDR_ob(offset, base));
|
||||
@ -1232,6 +1243,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void sbbl_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("sbbl %s, %s", GPReg32Name(src), GPReg32Name(dst));
|
||||
m_formatter.oneByteOp(OP_SBB_GvEv, src, dst);
|
||||
}
|
||||
|
||||
void subl_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("subl %s, %s", GPReg32Name(src), GPReg32Name(dst));
|
||||
|
@ -121,20 +121,28 @@ CodeGeneratorX86::visitUnbox(LUnbox* unbox)
|
||||
{
|
||||
// Note that for unbox, the type and payload indexes are switched on the
|
||||
// inputs.
|
||||
Operand type = ToOperand(unbox->type());
|
||||
Operand payload = ToOperand(unbox->payload());
|
||||
Register output = ToRegister(unbox->output());
|
||||
MUnbox* mir = unbox->mir();
|
||||
|
||||
JSValueTag tag = MIRTypeToTag(mir->type());
|
||||
if (mir->fallible()) {
|
||||
masm.cmp32(ToOperand(unbox->type()), Imm32(tag));
|
||||
masm.cmp32(type, Imm32(tag));
|
||||
bailoutIf(Assembler::NotEqual, unbox->snapshot());
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
Label ok;
|
||||
masm.branch32(Assembler::Equal, ToOperand(unbox->type()), Imm32(tag), &ok);
|
||||
masm.branch32(Assembler::Equal, type, Imm32(tag), &ok);
|
||||
masm.assumeUnreachable("Infallible unbox type mismatch");
|
||||
masm.bind(&ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Note: If spectreValueMasking is disabled, then this instruction will
|
||||
// default to a no-op as long as the lowering allocate the same register for
|
||||
// the output and the payload.
|
||||
masm.unboxNonDouble(type, payload, output, ValueTypeFromMIRType(mir->type()));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -119,8 +119,16 @@ LIRGeneratorX86::visitUnbox(MUnbox* unbox)
|
||||
|
||||
// Swap the order we use the box pieces so we can re-use the payload register.
|
||||
LUnbox* lir = new(alloc()) LUnbox;
|
||||
lir->setOperand(0, usePayloadInRegisterAtStart(inner));
|
||||
lir->setOperand(1, useType(inner, LUse::ANY));
|
||||
bool reusePayloadReg = !JitOptions.spectreValueMasking ||
|
||||
unbox->type() == MIRType::Int32 ||
|
||||
unbox->type() == MIRType::Boolean;
|
||||
if (reusePayloadReg) {
|
||||
lir->setOperand(0, usePayloadInRegisterAtStart(inner));
|
||||
lir->setOperand(1, useType(inner, LUse::ANY));
|
||||
} else {
|
||||
lir->setOperand(0, usePayload(inner, LUse::REGISTER));
|
||||
lir->setOperand(1, useType(inner, LUse::ANY));
|
||||
}
|
||||
|
||||
if (unbox->fallible())
|
||||
assignSnapshot(lir, unbox->bailoutKind());
|
||||
@ -130,7 +138,10 @@ LIRGeneratorX86::visitUnbox(MUnbox* unbox)
|
||||
// recoverable. Unbox's purpose is to eagerly kill the definition of a type
|
||||
// tag, so keeping both alive (for the purpose of gcmaps) is unappealing.
|
||||
// Instead, we create a new virtual register.
|
||||
defineReuseInput(lir, unbox, 0);
|
||||
if (reusePayloadReg)
|
||||
defineReuseInput(lir, unbox, 0);
|
||||
else
|
||||
define(lir, unbox);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -680,15 +680,67 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
movl(ImmType(type), dest.typeReg());
|
||||
}
|
||||
|
||||
void unboxNonDouble(const ValueOperand& src, Register dest, JSValueType type) {
|
||||
if (src.payloadReg() != dest)
|
||||
movl(src.payloadReg(), dest);
|
||||
void unboxNonDouble(const ValueOperand& src, Register dest, JSValueType type, Register scratch = InvalidReg) {
|
||||
unboxNonDouble(Operand(src.typeReg()), Operand(src.payloadReg()), dest, type, scratch);
|
||||
}
|
||||
void unboxNonDouble(const Operand& tag, const Operand& payload, Register dest, JSValueType type, Register scratch = InvalidReg) {
|
||||
auto movPayloadToDest = [&]() {
|
||||
if (payload.kind() != Operand::REG || !payload.containsReg(dest))
|
||||
movl(payload, dest);
|
||||
};
|
||||
if (!JitOptions.spectreValueMasking) {
|
||||
movPayloadToDest();
|
||||
return;
|
||||
}
|
||||
|
||||
// Spectre mitigation: We zero the payload if the tag does not match the
|
||||
// expected type and if this is a pointer type.
|
||||
if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
|
||||
movPayloadToDest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tag.containsReg(dest) && !payload.containsReg(dest)) {
|
||||
// We zero the destination register and move the payload into it if
|
||||
// the tag corresponds to the given type.
|
||||
xorl(dest, dest);
|
||||
cmpl(Imm32(JSVAL_TYPE_TO_TAG(type)), tag);
|
||||
cmovCCl(Condition::Equal, payload, dest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scratch == InvalidReg || scratch == dest ||
|
||||
tag.containsReg(scratch) || payload.containsReg(scratch))
|
||||
{
|
||||
// UnboxedLayout::makeConstructorCode calls extractObject with a
|
||||
// scratch register which aliases the tag register, thus we cannot
|
||||
// assert the above condition.
|
||||
scratch = InvalidReg;
|
||||
}
|
||||
|
||||
// The destination register aliases one of the operands. We create a
|
||||
// zero value either in a scratch register or on the stack and use it
|
||||
// to reset the destination register after reading both the tag and the
|
||||
// payload.
|
||||
Operand zero(Address(esp, 0));
|
||||
if (scratch == InvalidReg) {
|
||||
push(Imm32(0));
|
||||
} else {
|
||||
xorl(scratch, scratch);
|
||||
zero = Operand(scratch);
|
||||
}
|
||||
cmpl(Imm32(JSVAL_TYPE_TO_TAG(type)), tag);
|
||||
movPayloadToDest();
|
||||
cmovCCl(Condition::NotEqual, zero, dest);
|
||||
if (scratch == InvalidReg) {
|
||||
addl(Imm32(sizeof(void*)), esp);
|
||||
}
|
||||
}
|
||||
void unboxNonDouble(const Address& src, Register dest, JSValueType type) {
|
||||
movl(payloadOf(src), dest);
|
||||
unboxNonDouble(tagOf(src), payloadOf(src), dest, type);
|
||||
}
|
||||
void unboxNonDouble(const BaseIndex& src, Register dest, JSValueType type) {
|
||||
movl(payloadOf(src), dest);
|
||||
unboxNonDouble(tagOf(src), payloadOf(src), dest, type);
|
||||
}
|
||||
void unboxInt32(const ValueOperand& src, Register dest) {
|
||||
unboxNonDouble(src, dest, JSVAL_TYPE_INT32);
|
||||
@ -771,17 +823,20 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
// Extended unboxing API. If the payload is already in a register, returns
|
||||
// that register. Otherwise, provides a move to the given scratch register,
|
||||
// and returns that.
|
||||
Register extractObject(const Address& address, Register scratch) {
|
||||
movl(payloadOf(address), scratch);
|
||||
return scratch;
|
||||
Register extractObject(const Address& address, Register dest) {
|
||||
unboxObject(address, dest);
|
||||
return dest;
|
||||
}
|
||||
Register extractObject(const ValueOperand& value, Register scratch) {
|
||||
unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_OBJECT, scratch);
|
||||
return value.payloadReg();
|
||||
}
|
||||
Register extractString(const ValueOperand& value, Register scratch) {
|
||||
unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_STRING, scratch);
|
||||
return value.payloadReg();
|
||||
}
|
||||
Register extractSymbol(const ValueOperand& value, Register scratch) {
|
||||
unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_SYMBOL, scratch);
|
||||
return value.payloadReg();
|
||||
}
|
||||
Register extractInt32(const ValueOperand& value, Register scratch) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user