1use std::collections::BTreeMap;
2
3use crate::*;
4
5pub struct Wasi {
7 pub ctx: wasmtime_wasi::WasiCtx,
9
10 #[cfg(feature = "nn")]
12 pub nn: wasmtime_wasi_nn::WasiNnCtx,
13}
14
15pub struct Internal {
17 pub store: *mut Store<Internal>,
19
20 pub linker: *mut wasmtime::Linker<Internal>,
22
23 pub wasi: Option<Wasi>,
25
26 pub http_status: u16,
28
29 pub vars: BTreeMap<String, Vec<u8>>,
31
32 pub manifest: Manifest,
33
34 pub available_pages: Option<u32>,
35
36 pub(crate) memory_limiter: Option<MemoryLimiter>,
37}
38
39pub trait InternalExt {
41 fn store(&self) -> &Store<Internal>;
42
43 fn store_mut(&mut self) -> &mut Store<Internal>;
44
45 fn linker(&self) -> &Linker<Internal>;
46
47 fn linker_mut(&mut self) -> &mut Linker<Internal>;
48
49 fn linker_and_store(&mut self) -> (&mut Linker<Internal>, &mut Store<Internal>);
50
51 fn internal(&self) -> &Internal {
52 self.store().data()
53 }
54
55 fn internal_mut(&mut self) -> &mut Internal {
56 self.store_mut().data_mut()
57 }
58
59 fn memory_ptr(&mut self) -> *mut u8 {
60 let (linker, mut store) = self.linker_and_store();
61 if let Some(mem) = linker.get(&mut store, "env", "memory") {
62 if let Some(mem) = mem.into_memory() {
63 return mem.data_ptr(&mut store);
64 }
65 }
66
67 std::ptr::null_mut()
68 }
69
70 fn memory(&mut self) -> &mut [u8] {
71 let (linker, mut store) = self.linker_and_store();
72 let mem = linker
73 .get(&mut store, "env", "memory")
74 .unwrap()
75 .into_memory()
76 .unwrap();
77 let ptr = mem.data_ptr(&store);
78 if ptr.is_null() {
79 return &mut [];
80 }
81 let size = mem.data_size(&store);
82 unsafe { std::slice::from_raw_parts_mut(ptr, size) }
83 }
84
85 fn memory_read(&mut self, offs: u64, len: Size) -> &[u8] {
86 trace!("memory_read: {}, {}", offs, len);
87 let offs = offs as usize;
88 let len = len as usize;
89 let mem = self.memory();
90 &mem[offs..offs + len]
91 }
92
93 fn memory_read_str(&mut self, offs: u64) -> Result<&str, std::str::Utf8Error> {
94 let len = self.memory_length(offs);
95 std::str::from_utf8(self.memory_read(offs, len))
96 }
97
98 fn memory_write(&mut self, offs: u64, bytes: impl AsRef<[u8]>) {
99 trace!("memory_write: {}", offs);
100 let b = bytes.as_ref();
101 let offs = offs as usize;
102 let len = b.len();
103 self.memory()[offs..offs + len].copy_from_slice(bytes.as_ref());
104 }
105
106 fn memory_alloc(&mut self, n: Size) -> Result<u64, Error> {
107 if n == 0 {
108 return Ok(0);
109 }
110 let (linker, mut store) = self.linker_and_store();
111 let output = &mut [Val::I64(0)];
112 linker
113 .get(&mut store, "env", "extism_alloc")
114 .unwrap()
115 .into_func()
116 .unwrap()
117 .call(&mut store, &[Val::I64(n as i64)], output)?;
118 let offs = output[0].unwrap_i64() as u64;
119 if offs == 0 && n > 0 {
120 anyhow::bail!("out of memory")
121 }
122 trace!("memory_alloc: {}, {}", offs, n);
123 Ok(offs)
124 }
125
126 fn memory_alloc_bytes(&mut self, bytes: impl AsRef<[u8]>) -> Result<u64, Error> {
127 let b = bytes.as_ref();
128 let offs = self.memory_alloc(b.len() as Size)?;
129 self.memory_write(offs, b);
130 Ok(offs)
131 }
132
133 fn memory_free(&mut self, offs: u64) {
134 let (linker, mut store) = self.linker_and_store();
135 linker
136 .get(&mut store, "env", "extism_free")
137 .unwrap()
138 .into_func()
139 .unwrap()
140 .call(&mut store, &[Val::I64(offs as i64)], &mut [])
141 .unwrap();
142 }
143
144 fn memory_length(&mut self, offs: u64) -> u64 {
145 let (linker, mut store) = self.linker_and_store();
146 let output = &mut [Val::I64(0)];
147 linker
148 .get(&mut store, "env", "extism_length")
149 .unwrap()
150 .into_func()
151 .unwrap()
152 .call(&mut store, &[Val::I64(offs as i64)], output)
153 .unwrap();
154 let len = output[0].unwrap_i64() as u64;
155 trace!("memory_length: {}, {}", offs, len);
156 len
157 }
158
159 fn error<E>(&mut self, e: impl std::fmt::Debug, x: E) -> E {
161 let s = format!("{e:?}");
162 debug!("Set error: {:?}", s);
163 if let Ok(offs) = self.memory_alloc_bytes(&s) {
164 let (linker, mut store) = self.linker_and_store();
165 if let Some(f) = linker.get(&mut store, "env", "extism_error_set") {
166 f.into_func()
167 .unwrap()
168 .call(&mut store, &[Val::I64(offs as i64)], &mut [])
169 .unwrap();
170 }
171 }
172 x
173 }
174
175 fn clear_error(&mut self) {
176 let (linker, mut store) = self.linker_and_store();
177 if let Some(f) = linker.get(&mut store, "env", "extism_error_set") {
178 f.into_func()
179 .unwrap()
180 .call(&mut store, &[Val::I64(0)], &mut [])
181 .unwrap();
182 }
183 }
184
185 fn has_error(&mut self) -> bool {
186 let (linker, mut store) = self.linker_and_store();
187 let output = &mut [Val::I64(0)];
188 linker
189 .get(&mut store, "env", "extism_error_get")
190 .unwrap()
191 .into_func()
192 .unwrap()
193 .call(&mut store, &[], output)
194 .unwrap();
195 output[0].unwrap_i64() != 0
196 }
197
198 fn get_error(&mut self) -> Option<&str> {
199 let (linker, mut store) = self.linker_and_store();
200 let output = &mut [Val::I64(0)];
201 linker
202 .get(&mut store, "env", "extism_error_get")
203 .unwrap()
204 .into_func()
205 .unwrap()
206 .call(&mut store, &[], output)
207 .unwrap();
208 let offs = output[0].unwrap_i64() as u64;
209 if offs == 0 {
210 return None;
211 }
212
213 let length = self.memory_length(offs);
214 let data = self.memory_read(offs, length);
215 let s = std::str::from_utf8(data);
216 match s {
217 Ok(s) => Some(s),
218 Err(_) => None,
219 }
220 }
221}
222
223impl Internal {
224 pub(crate) fn new(
225 manifest: Manifest,
226 wasi: bool,
227 available_pages: Option<u32>,
228 ) -> Result<Self, Error> {
229 let wasi = if wasi {
230 let auth = wasmtime_wasi::ambient_authority();
231 let mut ctx = wasmtime_wasi::WasiCtxBuilder::new();
232 for (k, v) in manifest.as_ref().config.iter() {
233 ctx = ctx.env(k, v)?;
234 }
235
236 if let Some(a) = &manifest.as_ref().allowed_paths {
237 for (k, v) in a.iter() {
238 let d = wasmtime_wasi::Dir::open_ambient_dir(k, auth)?;
239 ctx = ctx.preopened_dir(d, v)?;
240 }
241 }
242
243 #[cfg(feature = "nn")]
244 let nn = wasmtime_wasi_nn::WasiNnCtx::new()?;
245
246 Some(Wasi {
247 ctx: ctx.build(),
248 #[cfg(feature = "nn")]
249 nn,
250 })
251 } else {
252 None
253 };
254
255 let memory_limiter = if let Some(pgs) = available_pages {
256 let n = pgs as usize * 65536;
257 Some(MemoryLimiter {
258 max_bytes: n,
259 bytes_left: n,
260 })
261 } else {
262 None
263 };
264
265 Ok(Internal {
266 wasi,
267 manifest,
268 http_status: 0,
269 vars: BTreeMap::new(),
270 linker: std::ptr::null_mut(),
271 store: std::ptr::null_mut(),
272 available_pages,
273 memory_limiter,
274 })
275 }
276
277 pub fn linker(&self) -> &wasmtime::Linker<Internal> {
278 unsafe { &*self.linker }
279 }
280
281 pub fn linker_mut(&mut self) -> &mut wasmtime::Linker<Internal> {
282 unsafe { &mut *self.linker }
283 }
284}
285
286impl InternalExt for Internal {
287 fn store(&self) -> &Store<Internal> {
288 unsafe { &*self.store }
289 }
290
291 fn store_mut(&mut self) -> &mut Store<Internal> {
292 unsafe { &mut *self.store }
293 }
294
295 fn linker(&self) -> &Linker<Internal> {
296 unsafe { &*self.linker }
297 }
298
299 fn linker_mut(&mut self) -> &mut Linker<Internal> {
300 unsafe { &mut *self.linker }
301 }
302
303 fn linker_and_store(&mut self) -> (&mut Linker<Internal>, &mut Store<Internal>) {
304 unsafe { (&mut *self.linker, &mut *self.store) }
305 }
306}
307
308pub(crate) struct MemoryLimiter {
309 bytes_left: usize,
310 max_bytes: usize,
311}
312
313impl MemoryLimiter {
314 pub(crate) fn reset(&mut self) {
315 self.bytes_left = self.max_bytes;
316 }
317}
318
319impl wasmtime::ResourceLimiter for MemoryLimiter {
320 fn memory_growing(
321 &mut self,
322 current: usize,
323 desired: usize,
324 maximum: Option<usize>,
325 ) -> Result<bool> {
326 if let Some(max) = maximum {
327 if desired > max {
328 return Ok(false);
329 }
330 }
331
332 let d = desired - current;
333 if d > self.bytes_left {
334 return Ok(false);
335 }
336
337 self.bytes_left -= d;
338 Ok(true)
339 }
340
341 fn table_growing(&mut self, _current: u32, desired: u32, maximum: Option<u32>) -> Result<bool> {
342 if let Some(max) = maximum {
343 return Ok(desired <= max);
344 }
345
346 Ok(true)
347 }
348}