Commit a34ccb08 authored by Eric Holk's avatar Eric Holk Committed by Commit Bot

Reland "Add browser_tests for WebAssembly out-of-bounds behavior"

This is a reland of bbd7b898
Original change's description:
> Add browser_tests for WebAssembly out-of-bounds behavior
> 
> Trap-based bounds checking requires interaction with the crash reporter
> to catch out of bounds accesses. These browser tests make sure this
> behavior is working in the context of a full Chrome browser.
> 
> Bug: chromium:722585
> Change-Id: I1993540f85f5d3500681cd3fbd7a9af817fe4ce8
> Reviewed-on: https://chromium-review.googlesource.com/648363
> Commit-Queue: Eric Holk <eholk@chromium.org>
> Reviewed-by: Scott Violet <sky@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#503907}

Bug: chromium:722585
Change-Id: Ic76c769af88134be215127c190706f8ae4fb6ed1
Reviewed-on: https://chromium-review.googlesource.com/682114Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Commit-Queue: Eric Holk <eholk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#504399}
parent 817c65fe
......@@ -1615,6 +1615,7 @@ test("browser_tests") {
"gpu/webgl_infobar_browsertest.cc",
"ppapi/ppapi_browsertest.cc",
"ppapi/ppapi_filechooser_browsertest.cc",
"v8/wasm_trap_handler.cc",
]
deps += [
......
<!--
Copyright (c) 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!DOCTYPE html>
<html>
<head>
<script src="wasm_constants.js"></script>
<script src="wasm_module_builder.js"></script>
<script src="out_of_bounds.js"></script>
</head>
</html>
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains tests to exercise Wasm's in- and out- of bounds behavior.
function testPass() {
console.error("testPass");
domAutomationController.send(true);
};
function testFail() { return false; }
const module_bytes = (function createModule() {
const builder = new WasmModuleBuilder;
builder.addImportedMemory("external", "memory");
const peek = builder.addFunction("peek", kSig_i_i).exportFunc();
peek.body
.get_local(0)
.i32_load()
.end();
const poke = builder.addFunction("poke", kSig_v_ii).exportFunc();
poke.body
.get_local(0)
.get_local(1)
.i32_store()
.end();
const grow = builder.addFunction("grow", kSig_i_i).exportFunc();
grow.body
.get_local(0)
.grow_memory()
.end();
return builder.toBuffer();
})();
function assert_true(b) {
if (!b) {
throw new Error("assert_true failed");
}
}
function assert_throws(error_class, f) {
try {
f()
} catch (e) {
if (e instanceof error_class) {
return;
}
}
throw new Error(
"function was expected to throw " + error_class + " but did not");
}
function assert_equals(actual, expected) {
if (actual != expected) {
throw new Error("Expected " + expected + " but got " + actual);
}
}
function instantiate(memory) {
assert_true(module_bytes instanceof ArrayBuffer,
"module bytes should be an ArrayBuffer");
assert_true(memory instanceof WebAssembly.Memory,
"memory must be a WebAssembly.Memory");
return WebAssembly.instantiate(module_bytes, { external: { memory: memory } })
.then(result => result.instance);
}
function instantiatePages(num_pages) {
return instantiate(new WebAssembly.Memory({ initial: num_pages }));
}
function assert_oob(func) {
return assert_throws(WebAssembly.RuntimeError, func);
}
function define_promise_test(name, f) {
window[name] = function() {
try {
f()
.then(_ => domAutomationController.send(true))
.catch(function(e) {
console.error("uncaught exception: " + e);
domAutomationController.send(false)
})
} catch (e) {
console.error("uncaught exception: " + e);
domAutomationController.send(false);
}
}
}
define_promise_test("peek_in_bounds", function() {
return instantiatePages(1).then(function(instance) {
const peek = instance.exports.peek;
assert_equals(peek(0), 0);
assert_equals(peek(10000), 0);
assert_equals(peek(65532), 0);
});
});
define_promise_test("peek_out_of_bounds", function() {
return instantiatePages(1).then(function(instance) {
const peek = instance.exports.peek;
assert_oob(_ => peek(65536));
assert_oob(_ => peek(65535));
assert_oob(_ => peek(65534));
assert_oob(_ => peek(65533));
assert_oob(_ => peek(1 << 30));
assert_oob(_ => peek(3 << 30));
});
});
define_promise_test("peek_out_of_bounds_grow_memory_from_zero_js", function() {
const memory = new WebAssembly.Memory({initial: 0});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
assert_oob(_ => peek(0));
memory.grow(1);
assert_equals(peek(0), 0);
});
});
define_promise_test("peek_out_of_bounds_grow_memory_js", function() {
const memory = new WebAssembly.Memory({initial: 1});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
assert_oob(_ => peek(70000));
memory.grow(1);
assert_equals(peek(70000), 0);
});
});
define_promise_test("peek_out_of_bounds_grow_memory_from_zero_wasm",
function() {
const memory = new WebAssembly.Memory({initial: 0});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
const grow = instance.exports.grow;
assert_oob(_ => peek(0));
grow(1);
assert_equals(peek(0), 0);
});
});
define_promise_test("peek_out_of_bounds_grow_memory_wasm", function() {
const memory = new WebAssembly.Memory({initial: 1});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
const grow = instance.exports.grow;
assert_oob(_ => peek(70000));
grow(1);
assert_equals(peek(70000), 0);
});
});
define_promise_test("poke_in_bounds", function() {
return instantiatePages(1).then(function(instance) {
const peek = instance.exports.peek;
const poke = instance.exports.poke;
poke(0, 41);
poke(10000, 42);
poke(65532, 43);
assert_equals(peek(0), 41);
assert_equals(peek(10000), 42);
assert_equals(peek(65532), 43);
});
});
define_promise_test("poke_out_of_bounds", function() {
return instantiatePages(1).then(function(instance) {
const poke = instance.exports.poke;
assert_oob(_ => poke(65536, 0));
assert_oob(_ => poke(65535, 0));
assert_oob(_ => poke(65534, 0));
assert_oob(_ => poke(65533, 0));
assert_oob(_ => poke(1 << 30, 0));
assert_oob(_ => poke(3 << 30, 0));
});
});
define_promise_test("poke_out_of_bounds_grow_memory_from_zero_js", function() {
const memory = new WebAssembly.Memory({initial: 0});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
const poke = instance.exports.poke;
function check_poke(index, value) {
poke(index, value);
assert_equals(peek(index), value);
}
assert_oob(_ => poke(0, 42));
memory.grow(1);
check_poke(0, 42);
});
});
define_promise_test("poke_out_of_bounds_grow_memory_js", function() {
const memory = new WebAssembly.Memory({initial: 1});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
const poke = instance.exports.poke;
function check_poke(index, value) {
poke(index, value);
assert_equals(peek(index), value);
}
assert_oob(_ => poke(70000, 42));
memory.grow(1);
check_poke(70000, 42);
});
});
define_promise_test("poke_out_of_bounds_grow_memory_from_zero_wasm",
function() {
const memory = new WebAssembly.Memory({initial: 0});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
const poke = instance.exports.poke;
const grow = instance.exports.grow;
function check_poke(index, value) {
poke(index, value);
assert_equals(peek(index), value);
}
assert_oob(_ => poke(0, 42));
grow(1);
check_poke(0, 42);
});
});
define_promise_test("poke_out_of_bounds_grow_memory_wasm", function() {
const memory = new WebAssembly.Memory({initial: 1});
return instantiate(memory).then(function(instance) {
const peek = instance.exports.peek;
const poke = instance.exports.poke;
const grow = instance.exports.grow;
function check_poke(index, value) {
poke(index, value);
assert_equals(peek(index), value);
}
assert_oob(_ => poke(70000, 42));
grow(1);
check_poke(70000, 42);
});
});
This diff is collapsed.
This diff is collapsed.
eholk@chromium.org
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// These tests focus on Wasm out of bounds behavior to make sure trap-based
// bounds checks work when integrated with all of Chrome.
#include "base/base_switches.h"
#include "build/build_config.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
class WasmTrapHandler : public InProcessBrowserTest {
public:
WasmTrapHandler() {}
protected:
void RunJSTest(const std::string& js) const {
auto* const tab = browser()->tab_strip_model()->GetActiveWebContents();
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(tab, js, &result));
ASSERT_TRUE(result);
}
void RunJSTestAndEnsureTrapHandlerRan(const std::string& js) const {
#if defined(OS_LINUX) && defined(ARCH_CPU_X86_64) && !defined(OS_ANDROID)
const bool is_trap_handler_supported = true;
#else
const bool is_trap_handler_supported = false;
#endif
if (is_trap_handler_supported &&
base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) {
const auto* get_fault_count =
"domAutomationController.send(%GetWasmRecoveredTrapCount())";
int original_count = 0;
auto* const tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScriptAndExtractInt(tab, get_fault_count,
&original_count));
ASSERT_NO_FATAL_FAILURE(RunJSTest(js));
int new_count = 0;
ASSERT_TRUE(content::ExecuteScriptAndExtractInt(tab, get_fault_count,
&new_count));
ASSERT_GT(new_count, original_count);
} else {
ASSERT_NO_FATAL_FAILURE(RunJSTest(js));
}
}
private:
void SetUpCommandLine(base::CommandLine* command_line) override {
// kEnableCrashReporterForTesting only exists on POSIX systems
#if defined(OS_POSIX)
command_line->AppendSwitch(switches::kEnableCrashReporterForTesting);
#endif
command_line->AppendSwitchASCII(switches::kJavaScriptFlags,
"--allow-natives-syntax");
}
DISALLOW_COPY_AND_ASSIGN(WasmTrapHandler);
};
IN_PROC_BROWSER_TEST_F(WasmTrapHandler, OutOfBounds) {
ASSERT_TRUE(embedded_test_server()->Start());
const auto& url = embedded_test_server()->GetURL("/wasm/out_of_bounds.html");
ui_test_utils::NavigateToURL(browser(), url);
ASSERT_NO_FATAL_FAILURE(RunJSTest("peek_in_bounds()"));
ASSERT_NO_FATAL_FAILURE(
RunJSTestAndEnsureTrapHandlerRan("peek_out_of_bounds()"));
ASSERT_NO_FATAL_FAILURE(RunJSTestAndEnsureTrapHandlerRan(
"peek_out_of_bounds_grow_memory_from_zero_js()"));
ASSERT_NO_FATAL_FAILURE(
RunJSTestAndEnsureTrapHandlerRan("peek_out_of_bounds_grow_memory_js()"));
ASSERT_NO_FATAL_FAILURE(RunJSTestAndEnsureTrapHandlerRan(
"peek_out_of_bounds_grow_memory_from_zero_wasm()"));
ASSERT_NO_FATAL_FAILURE(RunJSTestAndEnsureTrapHandlerRan(
"peek_out_of_bounds_grow_memory_wasm()"));
ASSERT_NO_FATAL_FAILURE(RunJSTest("poke_in_bounds()"));
ASSERT_NO_FATAL_FAILURE(
RunJSTestAndEnsureTrapHandlerRan("poke_out_of_bounds()"));
ASSERT_NO_FATAL_FAILURE(RunJSTestAndEnsureTrapHandlerRan(
"poke_out_of_bounds_grow_memory_from_zero_js()"));
ASSERT_NO_FATAL_FAILURE(
RunJSTestAndEnsureTrapHandlerRan("poke_out_of_bounds_grow_memory_js()"));
ASSERT_NO_FATAL_FAILURE(RunJSTestAndEnsureTrapHandlerRan(
"poke_out_of_bounds_grow_memory_from_zero_wasm()"));
ASSERT_NO_FATAL_FAILURE(RunJSTestAndEnsureTrapHandlerRan(
"poke_out_of_bounds_grow_memory_wasm()"));
}
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