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.
This commit is contained in:
Alex Crichton 2018-05-21 11:23:46 -07:00
parent cfe7ebd463
commit dd76707ea1
3 changed files with 75 additions and 1 deletions

View File

@ -256,7 +256,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
const mem = getUint32Memory();\n\ const mem = getUint32Memory();\n\
const ptr = mem[retptr / 4];\n\ const ptr = mem[retptr / 4];\n\
const len = mem[retptr / 4 + 1];\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\ wasm.__wbindgen_free(ptr, len * {});\n\
return realRet;\n\ return realRet;\n\
", f, ty.size()); ", f, ty.size());

View File

@ -90,6 +90,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
if !arg.is_by_ref() { if !arg.is_by_ref() {
self.prelude(&format!("\ self.prelude(&format!("\
v{0} = v{0}.slice();\n\
wasm.__wbindgen_free({0}, {1} * {size});\ wasm.__wbindgen_free({0}, {1} * {size});\
", abi, abi2, size = ty.size())); ", abi, abi2, size = ty.size()));
self.cx.require_internal_export("__wbindgen_free")?; self.cx.require_internal_export("__wbindgen_free")?;

View File

@ -416,3 +416,76 @@ fn export_mut() {
.test(); .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<u32> {
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<u32>,
}
#[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();
}