From dd76707ea1a3e9d4d0738e198b4f2fc9d93a8126 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 21 May 2018 11:23:46 -0700 Subject: [PATCH] Prevent use-after-free with vectors Awhile back slices switched to being raw views into wasm memory, but this doens't work if we free the underlying memory unconditionally! Moving around a `Vec` is already moving a lot of data, so let's copy it onto the JS heap instead of leaving it in the wasm heap. --- crates/cli-support/src/js/js2rust.rs | 2 +- crates/cli-support/src/js/rust2js.rs | 1 + tests/all/slice.rs | 73 ++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/crates/cli-support/src/js/js2rust.rs b/crates/cli-support/src/js/js2rust.rs index be304751..a338618d 100644 --- a/crates/cli-support/src/js/js2rust.rs +++ b/crates/cli-support/src/js/js2rust.rs @@ -256,7 +256,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> { const mem = getUint32Memory();\n\ const ptr = mem[retptr / 4];\n\ const len = mem[retptr / 4 + 1];\n\ - const realRet = {}(ptr, len);\n\ + const realRet = {}(ptr, len).slice();\n\ wasm.__wbindgen_free(ptr, len * {});\n\ return realRet;\n\ ", f, ty.size()); diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index 5e2eeca0..3f08935d 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -90,6 +90,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { if !arg.is_by_ref() { self.prelude(&format!("\ + v{0} = v{0}.slice();\n\ wasm.__wbindgen_free({0}, {1} * {size});\ ", abi, abi2, size = ty.size())); self.cx.require_internal_export("__wbindgen_free")?; diff --git a/tests/all/slice.rs b/tests/all/slice.rs index e5e3f13f..617802af 100644 --- a/tests/all/slice.rs +++ b/tests/all/slice.rs @@ -416,3 +416,76 @@ fn export_mut() { .test(); } +#[test] +fn return_vec_ok() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub fn broken_vec() -> Vec { + vec![1, 2, 3, 4, 5, 6, 7, 8, 9] + } + + #[wasm_bindgen] + pub fn web_main() -> Application { + Application::new() + } + + #[wasm_bindgen] + pub struct Application { + thing: Vec, + } + + #[wasm_bindgen] + impl Application { + pub fn new() -> Application { + let mut thing = vec![]; + thing.push(0); + thing.push(0); + thing.push(0); + thing.push(0); + thing.push(0); + + Application { + thing: thing + } + } + pub fn tick(&mut self) { + self.thing = self.thing.clone(); + } + } + + pub fn main() { + } + "#) + .file("test.ts", r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + + export function test() { + let app = wasm.web_main(); + + for (let i = 0; i < 10; i++) { + app.tick(); + let bad = wasm.broken_vec(); + console.log("Received from rust:", i, bad); + assert.strictEqual(bad[0], 1); + assert.strictEqual(bad[1], 2); + assert.strictEqual(bad[2], 3); + assert.strictEqual(bad[3], 4); + assert.strictEqual(bad[4], 5); + assert.strictEqual(bad[5], 6); + assert.strictEqual(bad[6], 7); + assert.strictEqual(bad[7], 8); + assert.strictEqual(bad[8], 9); + } + } + "#) + .test(); +} +