Commit 2fcf6da4 authored by Deepti Gandluri's avatar Deepti Gandluri Committed by Commit Bot

[wasm] Add runtime checks for misaligned atomic accesses

BUG: v8:6532, chromium:874809
Change-Id: I55c00e8563741908cf0daf263152ce927ae18e7c
Reviewed-on: https://chromium-review.googlesource.com/1205812
Commit-Queue: Deepti Gandluri <gdeepti@chromium.org>
Reviewed-by: 's avatarBen Smith <binji@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55666}
parent 31142ba4
......@@ -1218,6 +1218,7 @@ namespace internal {
TFC(WasmToNumber, TypeConversion, 1) \
TFS(ThrowWasmTrapUnreachable) \
TFS(ThrowWasmTrapMemOutOfBounds) \
TFS(ThrowWasmTrapUnalignedAccess) \
TFS(ThrowWasmTrapDivByZero) \
TFS(ThrowWasmTrapDivUnrepresentable) \
TFS(ThrowWasmTrapRemByZero) \
......
......@@ -3042,6 +3042,29 @@ Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
graph()->NewNode(op, base, offset, val, Effect(), Control()));
}
Node* WasmGraphBuilder::CheckBoundsAndAlignment(
uint8_t access_size, Node* index, uint32_t offset,
wasm::WasmCodePosition position) {
// Atomic operations access the memory, need to be bound checked till
// TrapHandlers are enabled on atomic operations
index =
BoundsCheckMem(access_size, index, offset, position, kNeedsBoundsCheck);
Node* effective_address =
graph()->NewNode(mcgraph()->machine()->IntAdd(), MemBuffer(offset),
Uint32ToUintptr(index));
// Unlike regular memory accesses, unaligned memory accesses for atomic
// operations should trap
// Access sizes are in powers of two, calculate mod without using division
Node* cond =
graph()->NewNode(mcgraph()->machine()->WordAnd(), effective_address,
IntPtrConstant(access_size - 1));
TrapIfFalse(wasm::kTrapUnalignedAccess,
graph()->NewNode(mcgraph()->machine()->Word32Equal(), cond,
mcgraph()->Int32Constant(0)),
position);
return index;
}
Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index,
uint32_t offset,
wasm::WasmCodePosition position,
......@@ -3883,14 +3906,13 @@ Node* WasmGraphBuilder::Simd8x16ShuffleOp(const uint8_t shuffle[16],
Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
uint32_t alignment, uint32_t offset,
wasm::WasmCodePosition position) {
// TODO(gdeepti): Add alignment validation, traps on misalignment
Node* node;
switch (opcode) {
#define BUILD_ATOMIC_BINOP(Name, Operation, Type, Prefix) \
case wasm::kExpr##Name: { \
Node* index = \
BoundsCheckMem(wasm::ValueTypes::MemSize(MachineType::Type()), \
inputs[0], offset, position, kNeedsBoundsCheck); \
Node* index = CheckBoundsAndAlignment( \
wasm::ValueTypes::MemSize(MachineType::Type()), inputs[0], offset, \
position); \
node = graph()->NewNode( \
mcgraph()->machine()->Prefix##Atomic##Operation(MachineType::Type()), \
MemBuffer(offset), index, inputs[1], Effect(), Control()); \
......@@ -3901,9 +3923,9 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
#define BUILD_ATOMIC_CMP_EXCHG(Name, Type, Prefix) \
case wasm::kExpr##Name: { \
Node* index = \
BoundsCheckMem(wasm::ValueTypes::MemSize(MachineType::Type()), \
inputs[0], offset, position, kNeedsBoundsCheck); \
Node* index = CheckBoundsAndAlignment( \
wasm::ValueTypes::MemSize(MachineType::Type()), inputs[0], offset, \
position); \
node = graph()->NewNode( \
mcgraph()->machine()->Prefix##AtomicCompareExchange( \
MachineType::Type()), \
......@@ -3913,24 +3935,24 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
ATOMIC_CMP_EXCHG_LIST(BUILD_ATOMIC_CMP_EXCHG)
#undef BUILD_ATOMIC_CMP_EXCHG
#define BUILD_ATOMIC_LOAD_OP(Name, Type, Prefix) \
case wasm::kExpr##Name: { \
Node* index = \
BoundsCheckMem(wasm::ValueTypes::MemSize(MachineType::Type()), \
inputs[0], offset, position, kNeedsBoundsCheck); \
node = graph()->NewNode( \
mcgraph()->machine()->Prefix##AtomicLoad(MachineType::Type()), \
MemBuffer(offset), index, Effect(), Control()); \
break; \
#define BUILD_ATOMIC_LOAD_OP(Name, Type, Prefix) \
case wasm::kExpr##Name: { \
Node* index = CheckBoundsAndAlignment( \
wasm::ValueTypes::MemSize(MachineType::Type()), inputs[0], offset, \
position); \
node = graph()->NewNode( \
mcgraph()->machine()->Prefix##AtomicLoad(MachineType::Type()), \
MemBuffer(offset), index, Effect(), Control()); \
break; \
}
ATOMIC_LOAD_LIST(BUILD_ATOMIC_LOAD_OP)
#undef BUILD_ATOMIC_LOAD_OP
#define BUILD_ATOMIC_STORE_OP(Name, Type, Rep, Prefix) \
case wasm::kExpr##Name: { \
Node* index = \
BoundsCheckMem(wasm::ValueTypes::MemSize(MachineType::Type()), \
inputs[0], offset, position, kNeedsBoundsCheck); \
Node* index = CheckBoundsAndAlignment( \
wasm::ValueTypes::MemSize(MachineType::Type()), inputs[0], offset, \
position); \
node = graph()->NewNode( \
mcgraph()->machine()->Prefix##AtomicStore(MachineRepresentation::Rep), \
MemBuffer(offset), index, inputs[1], Effect(), Control()); \
......
......@@ -349,6 +349,8 @@ class WasmGraphBuilder {
// BoundsCheckMem receives a uint32 {index} node and returns a ptrsize index.
Node* BoundsCheckMem(uint8_t access_size, Node* index, uint32_t offset,
wasm::WasmCodePosition, EnforceBoundsCheck);
Node* CheckBoundsAndAlignment(uint8_t access_size, Node* index,
uint32_t offset, wasm::WasmCodePosition);
Node* Uint32ToUintptr(Node*);
const Operator* GetSafeLoadOperator(int offset, wasm::ValueType type);
const Operator* GetSafeStoreOperator(int offset, wasm::ValueType type);
......
......@@ -1570,6 +1570,7 @@ enum class LoadSensitivity {
#define FOREACH_WASM_TRAPREASON(V) \
V(TrapUnreachable) \
V(TrapMemOutOfBounds) \
V(TrapUnalignedAccess) \
V(TrapDivByZero) \
V(TrapDivUnrepresentable) \
V(TrapRemByZero) \
......
......@@ -736,6 +736,7 @@ class ErrorUtils : public AllStatic {
/* Wasm errors (currently Error) */ \
T(WasmTrapUnreachable, "unreachable") \
T(WasmTrapMemOutOfBounds, "memory access out of bounds") \
T(WasmTrapUnalignedAccess, "operation does not support unaligned accesses") \
T(WasmTrapDivByZero, "divide by zero") \
T(WasmTrapDivUnrepresentable, "divide result unrepresentable") \
T(WasmTrapRemByZero, "remainder by zero") \
......
......@@ -145,109 +145,109 @@ function Test8Op(operation, func) {
}
(function TestAtomicAdd() {
print("TestAtomicAdd");
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 0);
Test32Op(Add, wasmAdd);
})();
(function TestAtomicAdd16U() {
print("TestAtomicAdd16U");
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd16U, 1, 0);
Test16Op(Add, wasmAdd);
})();
(function TestAtomicAdd8U() {
print("TestAtomicAdd8U");
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd8U, 0, 0);
Test8Op(Add, wasmAdd);
})();
(function TestAtomicSub() {
print("TestAtomicSub");
print(arguments.callee.name);
let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub, 2, 0);
Test32Op(Sub, wasmSub);
})();
(function TestAtomicSub16U() {
print("TestAtomicSub16U");
print(arguments.callee.name);
let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub16U, 1, 0);
Test16Op(Sub, wasmSub);
})();
(function TestAtomicSub8U() {
print("TestAtomicSub8U");
print(arguments.callee.name);
let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub8U, 0, 0);
Test8Op(Sub, wasmSub);
})();
(function TestAtomicAnd() {
print("TestAtomicAnd");
print(arguments.callee.name);
let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd, 2, 0);
Test32Op(And, wasmAnd);
})();
(function TestAtomicAnd16U() {
print("TestAtomicAnd16U");
print(arguments.callee.name);
let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd16U, 1, 0);
Test16Op(And, wasmAnd);
})();
(function TestAtomicAnd8U() {
print("TestAtomicAnd8U");
print(arguments.callee.name);
let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd8U, 0, 0);
Test8Op(And, wasmAnd);
})();
(function TestAtomicOr() {
print("TestAtomicOr");
print(arguments.callee.name);
let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr, 2, 0);
Test32Op(Or, wasmOr);
})();
(function TestAtomicOr16U() {
print("TestAtomicOr16U");
print(arguments.callee.name);
let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr16U, 1, 0);
Test16Op(Or, wasmOr);
})();
(function TestAtomicOr8U() {
print("TestAtomicOr8U");
print(arguments.callee.name);
let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr8U, 0, 0);
Test8Op(Or, wasmOr);
})();
(function TestAtomicXor() {
print("TestAtomicXor");
print(arguments.callee.name);
let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor, 2, 0);
Test32Op(Xor, wasmXor);
})();
(function TestAtomicXor16U() {
print("TestAtomicXor16U");
print(arguments.callee.name);
let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor16U, 1, 0);
Test16Op(Xor, wasmXor);
})();
(function TestAtomicXor8U() {
print("TestAtomicXor8U");
print(arguments.callee.name);
let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor8U, 0, 0);
Test8Op(Xor, wasmXor);
})();
(function TestAtomicExchange() {
print("TestAtomicExchange");
print(arguments.callee.name);
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange, 2, 0);
Test32Op(Exchange, wasmExchange);
})();
(function TestAtomicExchange16U() {
print("TestAtomicExchange16U");
print(arguments.callee.name);
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange16U, 1, 0);
Test16Op(Exchange, wasmExchange);
})();
(function TestAtomicExchange8U() {
print("TestAtomicExchange8U");
print(arguments.callee.name);
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange8U, 0, 0);
Test8Op(Exchange, wasmExchange);
})();
......@@ -268,7 +268,7 @@ function TestCmpExchange(func, buffer, params, size) {
}
(function TestAtomicCompareExchange() {
print("TestAtomicCompareExchange");
print(arguments.callee.name);
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, 0);
let i32 = new Uint32Array(memory.buffer);
......@@ -277,7 +277,7 @@ function TestCmpExchange(func, buffer, params, size) {
})();
(function TestAtomicCompareExchange16U() {
print("TestAtomicCompareExchange16U");
print(arguments.callee.name);
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange16U, 1, 0);
let i16 = new Uint16Array(memory.buffer);
......@@ -286,7 +286,7 @@ function TestCmpExchange(func, buffer, params, size) {
})();
(function TestAtomicCompareExchange8U() {
print("TestAtomicCompareExchange8U");
print(arguments.callee.name);
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange8U, 0, 0);
let i8 = new Uint8Array(memory.buffer);
......@@ -303,7 +303,7 @@ function TestLoad(func, buffer, value, size) {
}
(function TestAtomicLoad() {
print("TestAtomicLoad");
print(arguments.callee.name);
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad, 2, 0);
let i32 = new Uint32Array(memory.buffer);
let value = 0xacedaced;
......@@ -311,7 +311,7 @@ function TestLoad(func, buffer, value, size) {
})();
(function TestAtomicLoad16U() {
print("TestAtomicLoad16U");
print(arguments.callee.name);
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0);
let i16 = new Uint16Array(memory.buffer);
let value = 0xaced;
......@@ -319,7 +319,7 @@ function TestLoad(func, buffer, value, size) {
})();
(function TestAtomicLoad8U() {
print("TestAtomicLoad8U");
print(arguments.callee.name);
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad8U, 0, 0);
let i8 = new Uint8Array(memory.buffer);
let value = 0xac;
......@@ -335,7 +335,7 @@ function TestStore(func, buffer, value, size) {
}
(function TestAtomicStore() {
print("TestAtomicStore");
print(arguments.callee.name);
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0);
let i32 = new Uint32Array(memory.buffer);
let value = 0xacedaced;
......@@ -343,7 +343,7 @@ function TestStore(func, buffer, value, size) {
})();
(function TestAtomicStore16U() {
print("TestAtomicStore16U");
print(arguments.callee.name);
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore16U, 1, 0);
let i16 = new Uint16Array(memory.buffer);
let value = 0xaced;
......@@ -351,7 +351,7 @@ function TestStore(func, buffer, value, size) {
})();
(function TestAtomicStore8U() {
print("TestAtomicStore8U");
print(arguments.callee.name);
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore8U, 0, 0);
let i8 = new Uint8Array(memory.buffer);
let value = 0xac;
......@@ -359,7 +359,7 @@ function TestStore(func, buffer, value, size) {
})();
(function TestAtomicLoadStoreOffset() {
print("TestAtomicLoadStoreOffset");
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let memory = new WebAssembly.Memory({
initial: 16, maximum: 128, shared: true});
......@@ -390,7 +390,7 @@ function TestStore(func, buffer, value, size) {
})();
(function TestAtomicOpinLoop() {
print("TestAtomicOpinLoop");
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let memory = new WebAssembly.Memory({
initial: 16, maximum: 128, shared: true});
......@@ -415,3 +415,20 @@ function TestStore(func, buffer, value, size) {
{m: {imported_mem: memory}}));
assertEquals(20, instance.exports.main());
})();
(function TestUnalignedAtomicAccesses() {
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 17);
assertTraps(kTrapUnalignedAccess, () => wasmAdd(4, 1001));
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0);
assertTraps(kTrapUnalignedAccess, () => wasmLoad(15));
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0);
assertTraps(kTrapUnalignedAccess, () => wasmStore(22, 5));
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, 0x16);
assertTraps(kTrapUnalignedAccess, () => wasmCmpExchange(11, 6, 5));
// Building functions with bad alignment should fail to compile
assertThrows(() => GetAtomicBinOpFunction(kExprI32AtomicSub16U, 3, 0),
WebAssembly.CompileError);
})();
......@@ -380,6 +380,7 @@ let kTrapFloatUnrepresentable = 5;
let kTrapFuncInvalid = 6;
let kTrapFuncSigMismatch = 7;
let kTrapTypeError = 8;
let kTrapUnalignedAccess = 9;
let kTrapMsgs = [
"unreachable",
......@@ -390,7 +391,8 @@ let kTrapMsgs = [
"float unrepresentable in integer range",
"invalid index into function table",
"function signature mismatch",
"wasm function signature contains illegal type"
"wasm function signature contains illegal type",
"operation does not support unaligned accesses"
];
function assertTraps(trap, code) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment