1e6ff8411STyler Rockwood #![cfg(all(not(target_os = "windows"), not(miri)))]
2e6ff8411STyler Rockwood use std::{
3e6ff8411STyler Rockwood alloc::{GlobalAlloc, Layout, System},
4e6ff8411STyler Rockwood ops::Range,
5e6ff8411STyler Rockwood ptr::NonNull,
6e6ff8411STyler Rockwood sync::Arc,
7e6ff8411STyler Rockwood };
8*94740588SNick Fitzgerald use wasmtime::error::Context as _;
9e6ff8411STyler Rockwood use wasmtime::*;
10e6ff8411STyler Rockwood
align_up(v: usize, align: usize) -> usize11e6ff8411STyler Rockwood fn align_up(v: usize, align: usize) -> usize {
12e6ff8411STyler Rockwood return (v + (align - 1)) & (!(align - 1));
13e6ff8411STyler Rockwood }
14e6ff8411STyler Rockwood
15e6ff8411STyler Rockwood struct CustomStack {
16e6ff8411STyler Rockwood base: NonNull<u8>,
17e6ff8411STyler Rockwood len: usize,
18e6ff8411STyler Rockwood }
19e6ff8411STyler Rockwood unsafe impl Send for CustomStack {}
20e6ff8411STyler Rockwood unsafe impl Sync for CustomStack {}
21e6ff8411STyler Rockwood impl CustomStack {
new(base: NonNull<u8>, len: usize) -> Self22e6ff8411STyler Rockwood fn new(base: NonNull<u8>, len: usize) -> Self {
23e6ff8411STyler Rockwood CustomStack { base, len }
24e6ff8411STyler Rockwood }
25e6ff8411STyler Rockwood }
26e6ff8411STyler Rockwood unsafe impl StackMemory for CustomStack {
top(&self) -> *mut u827e6ff8411STyler Rockwood fn top(&self) -> *mut u8 {
28e6ff8411STyler Rockwood unsafe { self.base.as_ptr().add(self.len) }
29e6ff8411STyler Rockwood }
range(&self) -> Range<usize>30e6ff8411STyler Rockwood fn range(&self) -> Range<usize> {
31e6ff8411STyler Rockwood let base = self.base.as_ptr() as usize;
32e6ff8411STyler Rockwood base..base + self.len
33e6ff8411STyler Rockwood }
guard_range(&self) -> Range<*mut u8>34110e70f3SAlex Crichton fn guard_range(&self) -> Range<*mut u8> {
35110e70f3SAlex Crichton std::ptr::null_mut()..std::ptr::null_mut()
36110e70f3SAlex Crichton }
37e6ff8411STyler Rockwood }
38e6ff8411STyler Rockwood
39e6ff8411STyler Rockwood // A creator that allocates stacks on the heap instead of mmap'ing.
40e6ff8411STyler Rockwood struct CustomStackCreator {
41e6ff8411STyler Rockwood memory: NonNull<u8>,
42e6ff8411STyler Rockwood size: usize,
43e6ff8411STyler Rockwood layout: Layout,
44e6ff8411STyler Rockwood }
45e6ff8411STyler Rockwood
46e6ff8411STyler Rockwood unsafe impl Send for CustomStackCreator {}
47e6ff8411STyler Rockwood unsafe impl Sync for CustomStackCreator {}
48e6ff8411STyler Rockwood impl CustomStackCreator {
new() -> Result<Self>49e6ff8411STyler Rockwood fn new() -> Result<Self> {
50e6ff8411STyler Rockwood // 1MB
51e6ff8411STyler Rockwood const MINIMUM_STACK_SIZE: usize = 1 * 1_024 * 1_024;
52e6ff8411STyler Rockwood let page_size = rustix::param::page_size();
53e6ff8411STyler Rockwood let size = align_up(MINIMUM_STACK_SIZE, page_size);
54e6ff8411STyler Rockwood // Add an extra page for the guard page
55e6ff8411STyler Rockwood let layout = Layout::from_size_align(size + page_size, page_size)
56e6ff8411STyler Rockwood .context("unable to compute stack layout")?;
57e6ff8411STyler Rockwood let memory = unsafe {
58e6ff8411STyler Rockwood let mem = System.alloc(layout);
59e6ff8411STyler Rockwood let notnull = NonNull::new(mem);
60e6ff8411STyler Rockwood if let Some(mem) = notnull {
61f7d16d81STyler Rockwood // It's required that stack memory is zeroed for wasmtime
62f7d16d81STyler Rockwood libc::memset(mem.as_ptr().cast(), 0, layout.size());
63e6ff8411STyler Rockwood // Mark guard page as protected
64e6ff8411STyler Rockwood rustix::mm::mprotect(
65e6ff8411STyler Rockwood mem.as_ptr().cast(),
66e6ff8411STyler Rockwood page_size,
67e6ff8411STyler Rockwood rustix::mm::MprotectFlags::empty(),
68e6ff8411STyler Rockwood )?;
69e6ff8411STyler Rockwood }
70e6ff8411STyler Rockwood notnull
71e6ff8411STyler Rockwood }
72e6ff8411STyler Rockwood .context("unable to allocate stack memory")?;
73e6ff8411STyler Rockwood Ok(CustomStackCreator {
74e6ff8411STyler Rockwood memory,
75e6ff8411STyler Rockwood size,
76e6ff8411STyler Rockwood layout,
77e6ff8411STyler Rockwood })
78e6ff8411STyler Rockwood }
range(&self) -> Range<usize>79e6ff8411STyler Rockwood fn range(&self) -> Range<usize> {
80e6ff8411STyler Rockwood let page_size = rustix::param::page_size();
81e6ff8411STyler Rockwood let base = unsafe { self.memory.as_ptr().add(page_size) as usize };
82e6ff8411STyler Rockwood base..base + self.size
83e6ff8411STyler Rockwood }
84e6ff8411STyler Rockwood }
85e6ff8411STyler Rockwood impl Drop for CustomStackCreator {
drop(&mut self)86e6ff8411STyler Rockwood fn drop(&mut self) {
87e6ff8411STyler Rockwood let page_size = rustix::param::page_size();
88e6ff8411STyler Rockwood unsafe {
89e6ff8411STyler Rockwood // Unprotect the guard page as the allocator could reuse it.
90e6ff8411STyler Rockwood rustix::mm::mprotect(
91e6ff8411STyler Rockwood self.memory.as_ptr().cast(),
92e6ff8411STyler Rockwood page_size,
93e6ff8411STyler Rockwood rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE,
94e6ff8411STyler Rockwood )
95e6ff8411STyler Rockwood .unwrap();
96e6ff8411STyler Rockwood System.dealloc(self.memory.as_ptr(), self.layout);
97e6ff8411STyler Rockwood }
98e6ff8411STyler Rockwood }
99e6ff8411STyler Rockwood }
100e6ff8411STyler Rockwood unsafe impl StackCreator for CustomStackCreator {
new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn StackMemory>>101980a136eSNick Fitzgerald fn new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn StackMemory>> {
102980a136eSNick Fitzgerald if zeroed {
103980a136eSNick Fitzgerald bail!("CustomStackCreator does not support stack zeroing");
104980a136eSNick Fitzgerald }
105e6ff8411STyler Rockwood if size != self.size {
106e6ff8411STyler Rockwood bail!("must use the size we allocated for this stack memory creator");
107e6ff8411STyler Rockwood }
108e6ff8411STyler Rockwood let page_size = rustix::param::page_size();
109e6ff8411STyler Rockwood // skip over the page size
110e6ff8411STyler Rockwood let base_ptr = unsafe { self.memory.as_ptr().add(page_size) };
111e6ff8411STyler Rockwood let base = NonNull::new(base_ptr).context("unable to compute stack base")?;
112e6ff8411STyler Rockwood Ok(Box::new(CustomStack::new(base, self.size)))
113e6ff8411STyler Rockwood }
114e6ff8411STyler Rockwood }
115e6ff8411STyler Rockwood
config() -> (Store<()>, Arc<CustomStackCreator>)116e6ff8411STyler Rockwood fn config() -> (Store<()>, Arc<CustomStackCreator>) {
117e6ff8411STyler Rockwood let stack_creator = Arc::new(CustomStackCreator::new().unwrap());
118e6ff8411STyler Rockwood let mut config = Config::new();
119e6ff8411STyler Rockwood config
120e6ff8411STyler Rockwood .max_wasm_stack(stack_creator.size / 2)
121e6ff8411STyler Rockwood .async_stack_size(stack_creator.size)
122e6ff8411STyler Rockwood .with_host_stack(stack_creator.clone());
123e6ff8411STyler Rockwood (
124e6ff8411STyler Rockwood Store::new(&Engine::new(&config).unwrap(), ()),
125e6ff8411STyler Rockwood stack_creator,
126e6ff8411STyler Rockwood )
127e6ff8411STyler Rockwood }
128e6ff8411STyler Rockwood
129e6ff8411STyler Rockwood #[tokio::test]
130e657756dSAlex Crichton #[cfg_attr(asan, ignore)]
called_on_custom_heap_stack() -> Result<()>131e6ff8411STyler Rockwood async fn called_on_custom_heap_stack() -> Result<()> {
132e6ff8411STyler Rockwood let (mut store, stack_creator) = config();
133e6ff8411STyler Rockwood let module = Module::new(
134e6ff8411STyler Rockwood store.engine(),
135e6ff8411STyler Rockwood r#"
136e6ff8411STyler Rockwood (module
137e6ff8411STyler Rockwood (import "host" "callback" (func $callback (result i64)))
138e6ff8411STyler Rockwood (func $f (result i64) (call $callback))
139e6ff8411STyler Rockwood (export "f" (func $f))
140e6ff8411STyler Rockwood )
141e6ff8411STyler Rockwood "#,
142e6ff8411STyler Rockwood )?;
143e6ff8411STyler Rockwood
1448652011fSNick Fitzgerald let ty = FuncType::new(store.engine(), [], [ValType::I64]);
145e6ff8411STyler Rockwood let host_func = Func::new(&mut store, ty, move |_caller, _params, results| {
146e6ff8411STyler Rockwood let foo = 42;
147e6ff8411STyler Rockwood // output an address on the stack
148e6ff8411STyler Rockwood results[0] = Val::I64((&foo as *const i32) as usize as i64);
149e6ff8411STyler Rockwood Ok(())
150e6ff8411STyler Rockwood });
151e6ff8411STyler Rockwood let export = wasmtime::Extern::Func(host_func);
152e6ff8411STyler Rockwood let instance = Instance::new_async(&mut store, &module, &[export]).await?;
153e6ff8411STyler Rockwood let mut results = [Val::I64(0)];
154e6ff8411STyler Rockwood instance
155e6ff8411STyler Rockwood .get_func(&mut store, "f")
156e6ff8411STyler Rockwood .context("missing function export")?
157e6ff8411STyler Rockwood .call_async(&mut store, &[], &mut results)
158e6ff8411STyler Rockwood .await?;
159e6ff8411STyler Rockwood // Make sure the stack address we wrote was within our custom stack range
160e6ff8411STyler Rockwood let stack_address = results[0].i64().unwrap() as usize;
161e6ff8411STyler Rockwood assert!(stack_creator.range().contains(&stack_address));
162e6ff8411STyler Rockwood Ok(())
163e6ff8411STyler Rockwood }
164