Commit 30aa7b07 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Add some documentation for bounds checks

And apply a minor change: the {end_offset} is currently always >= 1, and
we sometimes use {end_offset - 1}. Change this to compute the
{end_offset} to be one less than before, and use {Uint32LessThan}
instead of {Uint32LessThanOrEqual}.
This matches the documentation I added and makes reasoning about the
correctness of the checks easier (at least for me).

R=titzer@chromium.org

Change-Id: I9a18ad5c72895cbadb6593cb74d6edc24f9ab032
Reviewed-on: https://chromium-review.googlesource.com/852145
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50411}
parent 789f327d
...@@ -3555,19 +3555,31 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index, ...@@ -3555,19 +3555,31 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index,
if (access_size > max_size || offset > max_size - access_size) { if (access_size > max_size || offset > max_size - access_size) {
// The access will be out of bounds, even for the largest memory. // The access will be out of bounds, even for the largest memory.
TrapIfEq32(wasm::kTrapMemOutOfBounds, jsgraph()->Int32Constant(0), 0, TrapIfEq32(wasm::kTrapMemOutOfBounds, Int32Constant(0), 0, position);
position);
return jsgraph()->IntPtrConstant(0); return jsgraph()->IntPtrConstant(0);
} }
uint32_t end_offset = offset + access_size; DCHECK_LE(1, access_size);
// This computation cannot overflow, since
if (end_offset > min_size) { // {offset <= max_size - access_size <= kMaxUint32 - access_size}.
// It also cannot underflow, since {access_size >= 1}.
uint32_t end_offset = offset + access_size - 1;
Node* end_offset_node = Int32Constant(end_offset);
// The accessed memory is [index + offset, index + end_offset].
// Check that the last read byte (at {index + end_offset}) is in bounds.
// 1) Check that {end_offset < mem_size}. This also ensures that we can safely
// compute {effective_size} as {mem_size - end_offset)}.
// {effective_size} is >= 1 if condition 1) holds.
// 2) Check that {index + end_offset < mem_size} by
// - computing {effective_size} as {mem_size - end_offset} and
// - checking that {index < effective_size}.
if (end_offset >= min_size) {
// The end offset is larger than the smallest memory. // The end offset is larger than the smallest memory.
// Dynamically check the end offset against the actual memory size, which // Dynamically check the end offset against the actual memory size, which
// is not known at compile time. // is not known at compile time.
Node* cond = Node* cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThan(),
graph()->NewNode(jsgraph()->machine()->Uint32LessThanOrEqual(), end_offset_node, mem_size);
jsgraph()->Int32Constant(end_offset), mem_size);
TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
} else { } else {
// The end offset is within the bounds of the smallest memory, so only // The end offset is within the bounds of the smallest memory, so only
...@@ -3575,7 +3587,7 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index, ...@@ -3575,7 +3587,7 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index,
Uint32Matcher match(index); Uint32Matcher match(index);
if (match.HasValue()) { if (match.HasValue()) {
uint32_t index_val = match.Value(); uint32_t index_val = match.Value();
if (index_val <= min_size - end_offset) { if (index_val < min_size - end_offset) {
// The input index is a constant and everything is statically within // The input index is a constant and everything is statically within
// bounds of the smallest possible memory. // bounds of the smallest possible memory.
return Uint32ToUintptr(index); return Uint32ToUintptr(index);
...@@ -3583,13 +3595,9 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index, ...@@ -3583,13 +3595,9 @@ Node* WasmGraphBuilder::BoundsCheckMem(uint8_t access_size, Node* index,
} }
} }
// Compute the effective size of the memory, which is the size of the memory // This produces a positive number, since {end_offset < min_size <= mem_size}.
// minus the statically known offset, minus the byte size of the access minus Node* effective_size = graph()->NewNode(jsgraph()->machine()->Int32Sub(),
// one. mem_size, end_offset_node);
// This produces a positive number since {end_offset <= min_size <= mem_size}.
Node* effective_size =
graph()->NewNode(jsgraph()->machine()->Int32Sub(), mem_size,
jsgraph()->Int32Constant(end_offset - 1));
// Introduce the actual bounds check. // Introduce the actual bounds check.
Node* cond = graph()->NewNode(m->Uint32LessThan(), index, effective_size); Node* cond = graph()->NewNode(m->Uint32LessThan(), index, effective_size);
......
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