Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/fuel.rs
1690 views
1
use wasmtime::*;
2
use wasmtime_test_macros::wasmtime_test;
3
use wast::parser::{self, Parse, ParseBuffer, Parser};
4
use wast::token::Span;
5
6
mod kw {
7
wast::custom_keyword!(assert_fuel);
8
}
9
10
struct FuelWast<'a> {
11
assertions: Vec<(Span, u64, wast::core::Module<'a>)>,
12
}
13
14
impl<'a> Parse<'a> for FuelWast<'a> {
15
fn parse(parser: Parser<'a>) -> parser::Result<Self> {
16
let mut assertions = Vec::new();
17
while !parser.is_empty() {
18
assertions.push(parser.parens(|p| {
19
let span = p.parse::<kw::assert_fuel>()?.0;
20
Ok((span, p.parse()?, p.parens(|p| p.parse())?))
21
})?);
22
}
23
Ok(FuelWast { assertions })
24
}
25
}
26
27
#[wasmtime_test]
28
#[cfg_attr(miri, ignore)]
29
fn run(config: &mut Config) -> Result<()> {
30
config.consume_fuel(true);
31
let test = std::fs::read_to_string("tests/all/fuel.wast")?;
32
let buf = ParseBuffer::new(&test)?;
33
let mut wast = parser::parse::<FuelWast<'_>>(&buf)?;
34
for (span, fuel, module) in wast.assertions.iter_mut() {
35
let consumed = fuel_consumed(&config, &module.encode()?)?;
36
if consumed == *fuel {
37
continue;
38
}
39
let (line, col) = span.linecol_in(&test);
40
panic!(
41
"tests/all/fuel.wast:{}:{} - expected {} fuel, found {}",
42
line + 1,
43
col + 1,
44
fuel,
45
consumed
46
);
47
}
48
Ok(())
49
}
50
51
fn fuel_consumed(config: &Config, wasm: &[u8]) -> Result<u64> {
52
let engine = Engine::new(&config)?;
53
let module = Module::new(&engine, wasm)?;
54
let mut store = Store::new(&engine, ());
55
store.set_fuel(u64::MAX)?;
56
drop(Instance::new(&mut store, &module, &[]));
57
Ok(u64::MAX - store.get_fuel()?)
58
}
59
60
#[wasmtime_test(wasm_features(gc, function_references))]
61
#[cfg_attr(miri, ignore)]
62
fn iloop(config: &mut Config) -> Result<()> {
63
config.consume_fuel(true);
64
iloop_aborts(
65
&config,
66
r#"
67
(module
68
(start 0)
69
(func loop br 0 end)
70
)
71
"#,
72
)?;
73
iloop_aborts(
74
&config,
75
r#"
76
(module
77
(start 0)
78
(func loop i32.const 1 br_if 0 end)
79
)
80
"#,
81
)?;
82
iloop_aborts(
83
&config,
84
r#"
85
(module
86
(start 0)
87
(func loop i32.const 0 br_table 0 end)
88
)
89
"#,
90
)?;
91
iloop_aborts(
92
&config,
93
r#"
94
(module
95
(start 0)
96
(func $f0 call $f1 call $f1)
97
(func $f1 call $f2 call $f2)
98
(func $f2 call $f3 call $f3)
99
(func $f3 call $f4 call $f4)
100
(func $f4 call $f5 call $f5)
101
(func $f5 call $f6 call $f6)
102
(func $f6 call $f7 call $f7)
103
(func $f7 call $f8 call $f8)
104
(func $f8 call $f9 call $f9)
105
(func $f9 call $f10 call $f10)
106
(func $f10 call $f11 call $f11)
107
(func $f11 call $f12 call $f12)
108
(func $f12 call $f13 call $f13)
109
(func $f13 call $f14 call $f14)
110
(func $f14 call $f15 call $f15)
111
(func $f15 call $f16 call $f16)
112
(func $f16)
113
)
114
"#,
115
)?;
116
iloop_aborts(
117
&config,
118
r#"
119
(module
120
(start 0)
121
(func loop ref.null func br_on_null 0 drop end)
122
)
123
"#,
124
)?;
125
iloop_aborts(
126
&config,
127
r#"
128
(module
129
(start 0)
130
(func
131
ref.func 0
132
loop (param (ref func))
133
br_on_non_null 0
134
unreachable
135
end
136
)
137
(elem declare func 0)
138
)
139
"#,
140
)?;
141
iloop_aborts(
142
&config,
143
r#"
144
(module
145
(start 0)
146
(func
147
i32.const 0
148
ref.i31
149
loop (param (ref i31))
150
br_on_cast 0 anyref (ref i31)
151
unreachable
152
end
153
)
154
(elem declare func 0)
155
)
156
"#,
157
)?;
158
iloop_aborts(
159
&config,
160
r#"
161
(module
162
(start 0)
163
(func
164
ref.null any
165
loop (param anyref)
166
br_on_cast_fail 0 anyref (ref i31)
167
unreachable
168
end
169
)
170
(elem declare func 0)
171
)
172
"#,
173
)?;
174
iloop_aborts(
175
&config,
176
r#"
177
(module
178
(type $a (array i8))
179
(start 0)
180
(func
181
i32.const 0x400_0000
182
array.new_default $a
183
drop
184
)
185
)
186
"#,
187
)?;
188
189
fn iloop_aborts(config: &Config, wat: &str) -> Result<()> {
190
let engine = Engine::new(&config)?;
191
let module = Module::new(&engine, wat)?;
192
let mut store = Store::new(&engine, ());
193
store.set_fuel(10_000)?;
194
let error = Instance::new(&mut store, &module, &[]).err().unwrap();
195
assert_eq!(error.downcast::<Trap>().unwrap(), Trap::OutOfFuel);
196
Ok(())
197
}
198
199
Ok(())
200
}
201
202
#[wasmtime_test]
203
fn manual_fuel(config: &mut Config) -> Result<()> {
204
config.consume_fuel(true);
205
let engine = Engine::new(&config)?;
206
let mut store = Store::new(&engine, ());
207
store.set_fuel(10_000).unwrap();
208
assert_eq!(store.get_fuel().ok(), Some(10_000));
209
assert_eq!(store.set_fuel(1).ok(), Some(()));
210
assert_eq!(store.get_fuel().ok(), Some(1));
211
Ok(())
212
}
213
214
#[wasmtime_test]
215
#[cfg_attr(miri, ignore)]
216
fn host_function_consumes_all(config: &mut Config) -> Result<()> {
217
const FUEL: u64 = 10_000;
218
config.consume_fuel(true);
219
let engine = Engine::new(&config)?;
220
let module = Module::new(
221
&engine,
222
r#"
223
(module
224
(import "" "" (func))
225
(func (export "")
226
call 0
227
call $other)
228
(func $other))
229
"#,
230
)
231
.unwrap();
232
let mut store = Store::new(&engine, ());
233
store.set_fuel(FUEL).unwrap();
234
let func = Func::wrap(&mut store, |mut caller: Caller<'_, ()>| {
235
let remaining = caller.get_fuel().unwrap();
236
assert_eq!(remaining, FUEL - 2);
237
assert!(caller.set_fuel(1).is_ok());
238
});
239
240
let instance = Instance::new(&mut store, &module, &[func.into()]).unwrap();
241
let export = instance.get_typed_func::<(), ()>(&mut store, "").unwrap();
242
let trap = export.call(&mut store, ()).unwrap_err();
243
assert_eq!(trap.downcast::<Trap>().unwrap(), Trap::OutOfFuel);
244
Ok(())
245
}
246
247
#[wasmtime_test]
248
fn manual_edge_cases(config: &mut Config) -> Result<()> {
249
config.consume_fuel(true);
250
let engine = Engine::new(&config)?;
251
let mut store = Store::new(&engine, ());
252
store.set_fuel(u64::MAX).unwrap();
253
assert_eq!(store.get_fuel().unwrap(), u64::MAX);
254
Ok(())
255
}
256
257
#[wasmtime_test]
258
#[cfg_attr(miri, ignore)]
259
fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping(
260
config: &mut Config,
261
) -> Result<()> {
262
config.consume_fuel(true);
263
config.memory_reservation(0x1_0000);
264
265
let engine = Engine::new(&config)?;
266
267
let module = Module::new(
268
&engine,
269
r#"
270
(module
271
(memory 1 1)
272
(func (export "f") (param i32) (result i32)
273
local.get 0
274
local.get 0
275
i32.add
276
;; This offset is larger than our memory max size and therefore
277
;; will unconditionally trap.
278
i32.load8_s offset=0xffffffff))
279
"#,
280
)
281
.unwrap();
282
283
let mut store = Store::new(&engine, ());
284
let init_fuel = 1_000;
285
store.set_fuel(init_fuel).unwrap();
286
assert_eq!(init_fuel, store.get_fuel().unwrap());
287
288
let instance = Instance::new(&mut store, &module, &[]).unwrap();
289
let f = instance
290
.get_typed_func::<i32, i32>(&mut store, "f")
291
.unwrap();
292
293
let trap = f.call(&mut store, 0).unwrap_err();
294
assert_eq!(trap.downcast::<Trap>().unwrap(), Trap::MemoryOutOfBounds);
295
296
// The `i32.add` consumed some fuel before the unconditionally trapping
297
// memory access.
298
let consumed_fuel = init_fuel - store.get_fuel().unwrap();
299
assert!(consumed_fuel > 0);
300
Ok(())
301
}
302
303
#[wasmtime_test]
304
#[cfg_attr(miri, ignore)]
305
fn get_fuel_clamps_at_zero(config: &mut Config) -> Result<()> {
306
config.consume_fuel(true);
307
let engine = Engine::new(config)?;
308
let mut store = Store::new(&engine, ());
309
let module = Module::new(
310
&engine,
311
r#"
312
(module
313
(func $add2 (export "add2") (param $n i32) (result i32)
314
(i32.add (local.get $n) (i32.const 2))
315
)
316
)
317
"#,
318
)?;
319
let instance = Instance::new(&mut store, &module, &[])?;
320
321
let add2 = instance.get_typed_func::<i32, i32>(&mut store, "add2")?;
322
323
// Start with 6 fuel and one invocation of this function should cost 4 fuel
324
store.set_fuel(6)?;
325
assert_eq!(store.get_fuel()?, 6);
326
add2.call(&mut store, 10)?;
327
assert_eq!(store.get_fuel()?, 2);
328
329
// One more invocation of the function would technically take us to -2 fuel,
330
// but that's not representable, so the store should report 0 fuel after
331
// this completes.
332
add2.call(&mut store, 10)?;
333
assert_eq!(store.get_fuel()?, 0);
334
335
// Any further attempts should fail.
336
assert!(add2.call(&mut store, 10).is_err());
337
338
Ok(())
339
}
340
341
#[wasmtime_test]
342
#[cfg_attr(miri, ignore)]
343
fn immediate_trap_with_fuel1(config: &mut Config) -> Result<()> {
344
config.consume_fuel(true);
345
let engine = Engine::new(config)?;
346
let mut store = Store::new(&engine, ());
347
348
let module = Module::new(
349
&engine,
350
r#"
351
(module
352
(func (export "main"))
353
)
354
"#,
355
)?;
356
357
let instance = Instance::new(&mut store, &module, &[])?;
358
let main = instance.get_typed_func::<(), ()>(&mut store, "main")?;
359
store.set_fuel(1)?;
360
361
assert!(main.call(&mut store, ()).is_err());
362
363
Ok(())
364
}
365
366
#[wasmtime_test(strategies(only(Winch)))]
367
#[cfg_attr(miri, ignore)]
368
fn ensure_stack_alignment(config: &mut Config) -> Result<()> {
369
config.consume_fuel(true);
370
let engine = Engine::new(config)?;
371
let mut store = Store::new(&engine, ());
372
store.set_fuel(100000000)?;
373
374
let bytes = include_bytes!("../misc_testsuite/winch/fuel_stack_alignment.wat");
375
let module = Module::new(&engine, bytes)?;
376
let instance = Instance::new(&mut store, &module, &[])?;
377
let func = instance.get_typed_func::<f32, ()>(&mut store, "")?;
378
let trap = func.call(&mut store, 50397184.0).unwrap_err();
379
assert_eq!(
380
trap.downcast::<Trap>().unwrap(),
381
Trap::UnreachableCodeReached
382
);
383
Ok(())
384
}
385
386