Commit c48f40d0 authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[wasm] Decoder should narrow unreachable types on the stack.

R=rossberg@chromium.org

Bug: v8:6651
Change-Id: Iaa9217cacded9bdd3f0a35775275e79c231c272a
Reviewed-on: https://chromium-review.googlesource.com/642969Reviewed-by: 's avatarAndreas Rossberg <rossberg@chromium.org>
Commit-Queue: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47709}
parent 89b6ef0c
...@@ -1118,9 +1118,9 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1118,9 +1118,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return &stack_[stack_.size() - depth - 1]; return &stack_[stack_.size() - depth - 1];
} }
inline const Value& GetMergeValueFromStack(Control* c, size_t i) { inline Value& GetMergeValueFromStack(Control* c, size_t i) {
DCHECK_GT(c->merge.arity, i); DCHECK_GT(c->merge.arity, i);
DCHECK_GE(stack_.size(), c->merge.arity); DCHECK_GE(stack_.size(), c->stack_depth + c->merge.arity);
return stack_[stack_.size() - c->merge.arity + i]; return stack_[stack_.size() - c->merge.arity + i];
} }
...@@ -2037,26 +2037,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2037,26 +2037,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
int startrel(const byte* ptr) { return static_cast<int>(ptr - this->start_); } int startrel(const byte* ptr) { return static_cast<int>(ptr - this->start_); }
bool TypeCheckBreak(unsigned depth) {
DCHECK(validate); // Only call this for validation.
Control* c = control_at(depth);
if (c->is_loop()) {
// This is the inner loop block, which does not have a value.
return true;
}
size_t expected = control_.back().stack_depth + c->merge.arity;
if (stack_.size() < expected && !control_.back().unreachable) {
this->errorf(
this->pc_,
"expected at least %u values on the stack for br to @%d, found %d",
c->merge.arity, startrel(c->pc),
static_cast<int>(stack_.size() - c->stack_depth));
return false;
}
return TypeCheckMergeValues(c);
}
void FallThruTo(Control* c) { void FallThruTo(Control* c) {
DCHECK_EQ(c, &control_.back()); DCHECK_EQ(c, &control_.back());
if (!TypeCheckFallThru(c)) return; if (!TypeCheckFallThru(c)) return;
...@@ -2066,19 +2046,24 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2066,19 +2046,24 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
bool TypeCheckMergeValues(Control* c) { bool TypeCheckMergeValues(Control* c) {
// Typecheck the values left on the stack. DCHECK_GE(stack_.size(), c->stack_depth + c->merge.arity);
size_t avail = stack_.size() - c->stack_depth; // Typecheck the topmost {c->merge.arity} values on the stack.
size_t start = avail >= c->merge.arity ? 0 : c->merge.arity - avail; for (size_t i = 0; i < c->merge.arity; ++i) {
for (size_t i = start; i < c->merge.arity; ++i) {
auto& val = GetMergeValueFromStack(c, i); auto& val = GetMergeValueFromStack(c, i);
auto& old = c->merge[i]; auto& old = c->merge[i];
if (val.type != old.type && val.type != kWasmVar) { if (val.type != old.type) {
if (val.type == kWasmVar) {
// if {val.type} is polymorphic, which results from unreachable, make
// it more specific by using the merge value's expected type.
val.type = old.type;
} else {
this->errorf( this->errorf(
this->pc_, "type error in merge[%zu] (expected %s, got %s)", i, this->pc_, "type error in merge[%zu] (expected %s, got %s)", i,
WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type)); WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
return false; return false;
} }
} }
}
return true; return true;
} }
...@@ -2086,10 +2071,10 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2086,10 +2071,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
bool TypeCheckFallThru(Control* c) { bool TypeCheckFallThru(Control* c) {
DCHECK_EQ(c, &control_.back()); DCHECK_EQ(c, &control_.back());
if (!validate) return true; if (!validate) return true;
// Fallthru must match arity exactly. size_t expected = c->merge.arity;
size_t expected = c->stack_depth + c->merge.arity; size_t actual = stack_.size() - c->stack_depth;
if (stack_.size() != expected && // Fallthrus must match the arity of the control exactly.
(stack_.size() > expected || !c->unreachable)) { if (!InsertUnreachablesIfNecessary(expected, actual) || actual > expected) {
this->errorf(this->pc_, this->errorf(this->pc_,
"expected %u elements on the stack for fallthru to @%d", "expected %u elements on the stack for fallthru to @%d",
c->merge.arity, startrel(c->pc)); c->merge.arity, startrel(c->pc));
...@@ -2099,6 +2084,42 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2099,6 +2084,42 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return TypeCheckMergeValues(c); return TypeCheckMergeValues(c);
} }
bool TypeCheckBreak(unsigned depth) {
DCHECK(validate); // Only call this for validation.
Control* c = control_at(depth);
if (c->is_loop()) {
// This is the inner loop block, which does not have a value.
return true;
}
// Breaks must have at least the number of values expected; can have more.
size_t expected = c->merge.arity;
size_t actual = stack_.size() - control_.back().stack_depth;
if (!InsertUnreachablesIfNecessary(expected, actual)) {
this->errorf(this->pc_, "expected %u elements on the stack for br to @%d",
c->merge.arity, startrel(c->pc));
return false;
}
return TypeCheckMergeValues(c);
}
inline bool InsertUnreachablesIfNecessary(size_t expected, size_t actual) {
if (actual < expected) {
if (control_.back().unreachable) {
// A slow path. When the actual number of values on the stack is less
// than the expected number of values and the current control is
// unreachable, insert unreachable values below the actual values.
// This simplifies {TypeCheckMergeValues}.
auto pos = stack_.begin() + (stack_.size() - actual);
stack_.insert(pos, (expected - actual), Value::Unreachable(this->pc_));
return true;
} else {
// There aren't enough values on the stack.
return false;
}
}
return true; // enough actual values are there.
}
virtual void onFirstError() { virtual void onFirstError() {
this->end_ = this->pc_; // Terminate decoding loop. this->end_ = this->pc_; // Terminate decoding loop.
TRACE(" !%s\n", this->error_msg_.c_str()); TRACE(" !%s\n", this->error_msg_.c_str());
......
...@@ -128,3 +128,5 @@ run(I, "U (block (iblock 0 0 brt01) drop)", [unr, ...block, ...iblock, ...zero, ...@@ -128,3 +128,5 @@ run(I, "U (block (iblock 0 0 brt01) drop)", [unr, ...block, ...iblock, ...zero,
run(V, "(iblock (iblock U 0 brt01)) drop", [...iblock, ...iblock, unr, ...zero, ...brt01, end, end, drop]); run(V, "(iblock (iblock U 0 brt01)) drop", [...iblock, ...iblock, unr, ...zero, ...brt01, end, end, drop]);
run(I, "(block (fblock U 0 brt01) drop)", [...iblock, ...fblock, unr, ...zero, ...brt01, end, drop, end]); run(I, "(block (fblock U 0 brt01) drop)", [...iblock, ...fblock, unr, ...zero, ...brt01, end, drop, end]);
run(I, "(iblock (fblock U 0 brt01) drop 0) drop", [...iblock, ...fblock, unr, ...zero, ...brt01, end, drop, ...zero, end, drop]); run(I, "(iblock (fblock U 0 brt01) drop 0) drop", [...iblock, ...fblock, unr, ...zero, ...brt01, end, drop, ...zero, end, drop]);
run(I, "(iblock (block (U brif 1))", [...iblock, ...block, unr, kExprBrIf, 0, end, end, kExprDrop]);
...@@ -1846,6 +1846,14 @@ TEST_F(FunctionBodyDecoderTest, BreakIfBinop_fail) { ...@@ -1846,6 +1846,14 @@ TEST_F(FunctionBodyDecoderTest, BreakIfBinop_fail) {
WASM_BLOCK_I(WASM_F32_ABS(WASM_BRV_IF(0, WASM_F32(0.0f), WASM_ZERO)))); WASM_BLOCK_I(WASM_F32_ABS(WASM_BRV_IF(0, WASM_F32(0.0f), WASM_ZERO))));
} }
TEST_F(FunctionBodyDecoderTest, BreakIfUnrNarrow) {
EXPECT_FAILURE_S(
sigs.f_ff(),
WASM_BLOCK_I(WASM_BRV_IF(0, WASM_UNREACHABLE, WASM_UNREACHABLE),
WASM_RETURN0),
WASM_F32(0.0));
}
TEST_F(FunctionBodyDecoderTest, BreakNesting1) { TEST_F(FunctionBodyDecoderTest, BreakNesting1) {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
// (block[2] (loop[2] (if (get p) break[N]) (set p 1)) p) // (block[2] (loop[2] (if (get p) break[N]) (set p 1)) p)
......
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