Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/executor/vm/tests.rs
Line
Count
Source (jump to first uncovered line)
1
// Smoldot
2
// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
15
// You should have received a copy of the GNU General Public License
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
#![cfg(test)]
19
20
// Here is a helpful link for the WAT format:
21
// <https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format>
22
23
#[test]
24
1
fn is_send() {
25
1
    // Makes sure that the virtual machine types implement `Send`.
26
2
    fn test<T: Send>() {}
_RINvNvNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm5testss_7is_send4testNtB6_14VirtualMachineEBa_
Line
Count
Source
26
1
    fn test<T: Send>() {}
_RINvNvNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm5testss_7is_send4testNtB6_23VirtualMachinePrototypeEBa_
Line
Count
Source
26
1
    fn test<T: Send>() {}
27
1
    test::<super::VirtualMachine>();
28
1
    test::<super::VirtualMachinePrototype>();
29
1
}
30
31
#[test]
32
1
fn basic_seems_to_work() {
33
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
34
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
35
2
            module_bytes: &include_bytes!("./test-polkadot-runtime-v9160.wasm")[..],
36
2
            exec_hint,
37
96
            symbols: &mut |_, _, _| Ok(0),
38
2
        })
39
2
        .unwrap();
40
2
41
2
        // Note that this test doesn't test much, as anything elaborate would require implementing
42
2
        // the Substrate/Polkadot allocator.
43
2
44
2
        let mut vm = prototype
45
2
            .prepare()
46
2
            .start(
47
2
                "Core_version",
48
2
                &[super::WasmValue::I32(0), super::WasmValue::I32(0)],
49
2
            )
50
2
            .unwrap();
51
2
52
2
        loop {
53
2
            match vm.run(None) {
54
                Ok(super::ExecOutcome::Finished {
55
                    return_value: Ok(_),
56
0
                }) => break,
57
                Ok(super::ExecOutcome::Finished {
58
                    return_value: Err(_),
59
0
                }) => panic!(),
60
2
                Ok(super::ExecOutcome::Interrupted { id: 0, .. }) => break,
61
0
                Ok(super::ExecOutcome::Interrupted { .. }) => panic!(),
62
0
                Err(_) => panic!(),
63
            }
64
        }
65
    }
66
1
}
67
68
#[test]
69
1
fn wat_not_accepted() {
70
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
71
2
        assert!(
matches!0
(
72
2
            super::VirtualMachinePrototype::new(super::Config {
73
2
                module_bytes: b"(module)",
74
2
                exec_hint,
75
2
                symbols: &mut |_, _, _| 
Ok(0)0
76
2
            }),
77
            Err(super::NewErr::InvalidWasm(_))
78
        ));
79
    }
80
1
}
81
82
#[test]
83
1
fn out_of_memory_access() {
84
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
85
2
        let module_bytes = [
86
2
            0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x00, 0x0b,
87
2
            0x06, 0x01, 0x00, 0x41, 0x03, 0x0b, 0x00,
88
2
        ];
89
2
90
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
91
2
            module_bytes: &module_bytes[..],
92
2
            exec_hint,
93
2
            symbols: &mut |_, _, _| 
Ok(0)0
94
2
        })
95
2
        .is_err());
96
    }
97
1
}
98
99
#[test]
100
1
fn has_start_function() {
101
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
102
2
        let module_bytes = [
103
2
            0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
104
2
            0x02, 0x09, 0x01, 0x01, 0x71, 0x03, 0x69, 0x6d, 0x70, 0x00, 0x00, 0x08, 0x01, 0x00,
105
2
        ];
106
2
107
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
108
2
            module_bytes: &module_bytes[..],
109
2
            exec_hint,
110
2
            symbols: &mut |_, _, _| Ok(0)
111
2
        })
112
2
        .is_err());
113
    }
114
1
}
115
116
#[test]
117
1
fn unsupported_type() {
118
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
119
2
        let module_bytes = [
120
2
            0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01,
121
2
            0x7b, 0x02, 0x0d, 0x01, 0x04, 0x74, 0x65, 0x73, 0x74, 0x04, 0x66, 0x75, 0x6e, 0x63,
122
2
            0x00, 0x00,
123
2
        ];
124
2
125
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
126
2
            module_bytes: &module_bytes[..],
127
2
            exec_hint,
128
2
            symbols: &mut |_, _, _| 
Ok(0)0
129
2
        })
130
2
        .is_err());
131
    }
132
1
}
133
134
#[test]
135
1
fn basic_host_function_return_value_works() {
136
1
    let module_bytes = wat::parse_str(
137
1
        r#"
138
1
    (module
139
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
140
1
        (import "env" "memory" (memory $mem 0 4096))
141
1
        (func (export "hello") (result i32)
142
1
            (call $host_hello (i32.const 3))
143
1
            (i32.const 2)
144
1
            i32.add
145
1
        )
146
1
    )
147
1
    "#,
148
1
    )
149
1
    .unwrap();
150
151
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
152
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
153
2
            module_bytes: &module_bytes,
154
2
            exec_hint,
155
2
            symbols: &mut |_, _, _| Ok(0),
156
2
        })
157
2
        .unwrap();
158
2
159
2
        let mut vm = prototype.prepare().start("hello", &[]).unwrap();
160
2
161
2
        let mut resume_value = None;
162
        loop {
163
4
            match vm.run(resume_value) {
164
                Ok(super::ExecOutcome::Finished {
165
2
                    return_value: Ok(value),
166
2
                }) => {
167
2
                    assert_eq!(value, Some(super::WasmValue::I32(5)));
168
2
                    break;
169
                }
170
                Ok(super::ExecOutcome::Finished {
171
                    return_value: Err(_),
172
0
                }) => panic!(),
173
2
                Ok(super::ExecOutcome::Interrupted { id: 0, params }) => {
174
2
                    assert_eq!(params, vec![super::WasmValue::I32(3)]);
175
2
                    resume_value = Some(super::WasmValue::I32(3));
176
                }
177
0
                Ok(super::ExecOutcome::Interrupted { .. }) => panic!(),
178
0
                Err(_) => panic!(),
179
            }
180
        }
181
    }
182
1
}
183
184
#[test]
185
1
fn no_memory() {
186
1
    let module_bytes = wat::parse_str(
187
1
        r#"
188
1
    (module
189
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
190
1
        (func (export "hello") (result i32)
191
1
            (call $host_hello (i32.const 3))
192
1
        )
193
1
    )
194
1
    "#,
195
1
    )
196
1
    .unwrap();
197
198
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
199
2
        assert!(
matches!0
(
200
2
            super::VirtualMachinePrototype::new(super::Config {
201
2
                module_bytes: &module_bytes,
202
2
                exec_hint,
203
2
                symbols: &mut |_, _, _| Ok(0)
204
2
            }),
205
            Err(super::NewErr::NoMemory)
206
        ));
207
    }
208
1
}
209
210
#[test]
211
1
fn memory_misnamed() {
212
1
    let module_bytes = wat::parse_str(
213
1
        r#"
214
1
    (module
215
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
216
1
        (import "env" "memoryyyyy" (memory $mem 0 4096))
217
1
        (func (export "hello") (result i32)
218
1
            (call $host_hello (i32.const 3))
219
1
        )
220
1
    )
221
1
    "#,
222
1
    )
223
1
    .unwrap();
224
225
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
226
2
        assert!(
matches!0
(
227
2
            super::VirtualMachinePrototype::new(super::Config {
228
2
                module_bytes: &module_bytes,
229
2
                exec_hint,
230
2
                symbols: &mut |_, _, _| Ok(0)
231
2
            }),
232
            Err(super::NewErr::MemoryNotNamedMemory)
233
        ));
234
    }
235
1
}
236
237
#[test]
238
1
fn memory_isnt_memory() {
239
1
    let module_bytes = wat::parse_str(
240
1
        r#"
241
1
    (module
242
1
        (func (export "memory") (result i32) i32.const 0)
243
1
    )
244
1
    "#,
245
1
    )
246
1
    .unwrap();
247
248
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
249
2
        assert!(
matches!0
(
250
2
            super::VirtualMachinePrototype::new(super::Config {
251
2
                module_bytes: &module_bytes,
252
2
                exec_hint,
253
2
                symbols: &mut |_, _, _| 
Ok(0)0
254
2
            }),
255
            Err(super::NewErr::MemoryIsntMemory)
256
        ));
257
    }
258
1
}
259
260
#[test]
261
1
fn two_memories() {
262
1
    let module_bytes = wat::parse_str(
263
1
        r#"
264
1
    (module
265
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
266
1
        (import "env" "memory" (memory $mem 0 4096))
267
1
        (memory (export "memory") 0 4096)
268
1
        (func (export "hello") (result i32)
269
1
            (call $host_hello (i32.const 3))
270
1
        )
271
1
    )
272
1
    "#,
273
1
    )
274
1
    .unwrap();
275
276
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
277
        // Note that at the moment this module fails to compile altogether because the
278
        // multi-memory Wasm proposal isn't finalized yet. Even once finalized, we want to deny
279
        // this feature in smoldot.
280
2
        assert!(
matches!0
(
281
2
            super::VirtualMachinePrototype::new(super::Config {
282
2
                module_bytes: &module_bytes,
283
2
                exec_hint,
284
2
                symbols: &mut |_, _, _| 
Ok(0)0
285
2
            }),
286
            Err(super::NewErr::InvalidWasm(_) | super::NewErr::TwoMemories)
287
        ));
288
    }
289
1
}
290
291
#[test]
292
1
fn exported_memory_works() {
293
1
    let module_bytes = wat::parse_str(
294
1
        r#"
295
1
    (module
296
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
297
1
        (memory (export "memory") 0 4096)
298
1
        (func (export "hello") (result i32)
299
1
            (call $host_hello (i32.const 3))
300
1
        )
301
1
    )
302
1
    "#,
303
1
    )
304
1
    .unwrap();
305
306
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
307
2
        super::VirtualMachinePrototype::new(super::Config {
308
2
            module_bytes: &module_bytes,
309
2
            exec_hint,
310
2
            symbols: &mut |_, _, _| Ok(0),
311
2
        })
312
2
        .unwrap();
313
2
    }
314
1
}
315
316
#[test]
317
1
fn unresolved_function() {
318
1
    let module_bytes = wat::parse_str(
319
1
        r#"
320
1
    (module
321
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
322
1
        (import "env" "memory" (memory $mem 0 4096))
323
1
        (func (export "hello") (result i32)
324
1
            (call $host_hello (i32.const 3))
325
1
        )
326
1
    )
327
1
    "#,
328
1
    )
329
1
    .unwrap();
330
331
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
332
2
        assert!(
matches!0
(
333
2
            super::VirtualMachinePrototype::new(super::Config {
334
2
                module_bytes: &module_bytes,
335
2
                exec_hint,
336
2
                symbols: &mut |_, _, _| Err(())
337
2
            }),
338
            Err(super::NewErr::UnresolvedFunctionImport { .. })
339
        ));
340
    }
341
1
}
342
343
#[test]
344
1
fn unsupported_signature() {
345
1
    let module_bytes = wat::parse_str(
346
1
        r#"
347
1
    (module
348
1
        (import "host" "hello" (func $host_hello (param i32) (param f64) (result i32)))
349
1
        (import "env" "memory" (memory $mem 0 4096))
350
1
        (func (export "hello") (result i32) i32.const 0)
351
1
    )
352
1
    "#,
353
1
    )
354
1
    .unwrap();
355
356
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
357
2
        assert!(
matches!0
(
358
2
            super::VirtualMachinePrototype::new(super::Config {
359
2
                module_bytes: &module_bytes,
360
2
                exec_hint,
361
2
                symbols: &mut |_, _, _| 
Ok(0)0
362
2
            }),
363
            Err(super::NewErr::UnresolvedFunctionImport { .. })
364
        ));
365
    }
366
1
}
367
368
#[test]
369
1
fn unsupported_import_type() {
370
1
    let module_bytes = wat::parse_str(
371
1
        r#"
372
1
    (module
373
1
        (import "env" "mytable" (table 1 funcref))
374
1
        (import "env" "memory" (memory $mem 0 4096))
375
1
        (func (export "hello") (result i32) i32.const 0)
376
1
    )
377
1
    "#,
378
1
    )
379
1
    .unwrap();
380
381
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
382
2
        assert!(
matches!0
(
383
2
            super::VirtualMachinePrototype::new(super::Config {
384
2
                module_bytes: &module_bytes,
385
2
                exec_hint,
386
2
                symbols: &mut |_, _, _| 
Ok(0)0
387
2
            }),
388
            Err(super::NewErr::ImportTypeNotSupported)
389
        ));
390
    }
391
1
}
392
393
#[test]
394
1
fn start_function_forbidden() {
395
1
    let module_bytes = wat::parse_str(
396
1
        r#"
397
1
    (module
398
1
        (import "env" "memory" (memory $mem 0 4096))
399
1
        (func $s)
400
1
        (start $s)
401
1
    )
402
1
    "#,
403
1
    )
404
1
    .unwrap();
405
406
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
407
        // TODO: `Ok(_)` shouldn't be accepted, but wasmtime doesn't really make it possible to detect the start function at the moment
408
2
        assert!(
matches!0
(
409
2
            super::VirtualMachinePrototype::new(super::Config {
410
2
                module_bytes: &module_bytes,
411
2
                exec_hint,
412
2
                symbols: &mut |_, _, _| 
Ok(0)0
413
2
            }),
414
            Err(super::NewErr::StartFunctionNotSupported) | Ok(_)
415
        ));
416
    }
417
1
}
418
419
#[test]
420
1
fn max_memory_pages() {
421
1
    let module_bytes = wat::parse_str(
422
1
        r#"
423
1
    (module
424
1
        (import "env" "memory" (memory $mem 0 4096))
425
1
        (global $g (export "test") i32 (i32.const 12))
426
1
    )
427
1
    "#,
428
1
    )
429
1
    .unwrap();
430
431
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
432
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
433
2
            module_bytes: &module_bytes,
434
2
            exec_hint,
435
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
436
2
        })
437
2
        .unwrap();
438
2
        assert_eq!(
439
2
            prototype.memory_max_pages().unwrap(),
440
2
            super::HeapPages::new(4096)
441
2
        );
442
    }
443
1
}
444
445
#[test]
446
1
fn get_global() {
447
1
    let module_bytes = wat::parse_str(
448
1
        r#"
449
1
    (module
450
1
        (import "env" "memory" (memory $mem 0 4096))
451
1
        (global (export "test") i32 (i32.const 12))
452
1
    )
453
1
    "#,
454
1
    )
455
1
    .unwrap();
456
457
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
458
2
        let mut prototype = super::VirtualMachinePrototype::new(super::Config {
459
2
            module_bytes: &module_bytes,
460
2
            exec_hint,
461
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
462
2
        })
463
2
        .unwrap();
464
2
        assert_eq!(prototype.global_value("test").unwrap(), 12);
465
    }
466
1
}
467
468
#[test]
469
1
fn global_doesntexist() {
470
1
    let module_bytes = wat::parse_str(
471
1
        r#"
472
1
    (module
473
1
        (import "env" "memory" (memory $mem 0 4096))
474
1
    )
475
1
    "#,
476
1
    )
477
1
    .unwrap();
478
479
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
480
2
        let mut prototype = super::VirtualMachinePrototype::new(super::Config {
481
2
            module_bytes: &module_bytes,
482
2
            exec_hint,
483
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
484
2
        })
485
2
        .unwrap();
486
2
        assert!(
matches!0
(
487
2
            prototype.global_value("test"),
488
            Err(super::GlobalValueErr::NotFound)
489
        ));
490
    }
491
1
}
492
493
#[test]
494
1
fn global_wrong_type() {
495
1
    let module_bytes = wat::parse_str(
496
1
        r#"
497
1
    (module
498
1
        (import "env" "memory" (memory $mem 0 4096))
499
1
        (global (export "test") i64 (i64.const 2))
500
1
    )
501
1
    "#,
502
1
    )
503
1
    .unwrap();
504
505
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
506
2
        let mut prototype = super::VirtualMachinePrototype::new(super::Config {
507
2
            module_bytes: &module_bytes,
508
2
            exec_hint,
509
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
510
2
        })
511
2
        .unwrap();
512
2
        assert!(
matches!0
(
513
2
            prototype.global_value("test"),
514
            Err(super::GlobalValueErr::Invalid)
515
        ));
516
    }
517
1
}
518
519
#[test]
520
1
fn global_isnt_global() {
521
1
    let module_bytes = wat::parse_str(
522
1
        r#"
523
1
    (module
524
1
        (import "env" "memory" (memory $mem 0 4096))
525
1
        (func (export "test") (result i32) i32.const 0)
526
1
    )
527
1
    "#,
528
1
    )
529
1
    .unwrap();
530
531
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
532
2
        let mut prototype = super::VirtualMachinePrototype::new(super::Config {
533
2
            module_bytes: &module_bytes,
534
2
            exec_hint,
535
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
536
2
        })
537
2
        .unwrap();
538
2
        assert!(
matches!0
(
539
2
            prototype.global_value("test"),
540
            Err(super::GlobalValueErr::NotFound)
541
        ));
542
    }
543
1
}
544
545
#[test]
546
1
fn global_negative_value_works() {
547
1
    let module_bytes = wat::parse_str(
548
1
        r#"
549
1
    (module
550
1
        (import "env" "memory" (memory $mem 0 4096))
551
1
        (global (export "test") i32 (i32.const -1))
552
1
    )
553
1
    "#,
554
1
    )
555
1
    .unwrap();
556
557
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
558
2
        let mut prototype = super::VirtualMachinePrototype::new(super::Config {
559
2
            module_bytes: &module_bytes,
560
2
            exec_hint,
561
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
562
2
        })
563
2
        .unwrap();
564
2
        assert_eq!(prototype.global_value("test").unwrap(), u32::MAX);
565
    }
566
1
}
567
568
#[test]
569
1
fn call_non_existing_function() {
570
1
    let module_bytes = wat::parse_str(
571
1
        r#"
572
1
    (module
573
1
        (import "env" "memory" (memory $mem 0 4096))
574
1
        (func (export "hello") (result i32) i32.const 0)
575
1
    )
576
1
    "#,
577
1
    )
578
1
    .unwrap();
579
580
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
581
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
582
2
            module_bytes: &module_bytes,
583
2
            exec_hint,
584
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
585
2
        })
586
2
        .unwrap();
587
2
        assert!(
matches!0
(
588
2
            prototype.prepare().start("doesntexist", &[]),
589
            Err((super::StartErr::FunctionNotFound, _))
590
        ));
591
    }
592
1
}
593
594
#[test]
595
1
fn call_signature_not_supported() {
596
1
    let module_bytes = wat::parse_str(
597
1
        r#"
598
1
    (module
599
1
        (import "env" "memory" (memory $mem 0 4096))
600
1
        (func (export "hello") (result f64) unreachable)
601
1
    )
602
1
    "#,
603
1
    )
604
1
    .unwrap();
605
606
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
607
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
608
2
            module_bytes: &module_bytes,
609
2
            exec_hint,
610
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
611
2
        })
612
2
        .unwrap();
613
2
        assert!(
matches!0
(
614
2
            prototype.prepare().start("hello", &[]),
615
            Err((super::StartErr::SignatureNotSupported, _))
616
        ));
617
    }
618
1
}
619
620
#[test]
621
1
fn bad_params_types() {
622
1
    let module_bytes = wat::parse_str(
623
1
        r#"
624
1
    (module
625
1
        (import "env" "memory" (memory $mem 0 4096))
626
1
        (func (export "hello") (param i32) (result i32) i32.const 0)
627
1
    )
628
1
    "#,
629
1
    )
630
1
    .unwrap();
631
632
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
633
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
634
2
            module_bytes: &module_bytes,
635
2
            exec_hint,
636
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
637
2
        })
638
2
        .unwrap();
639
2
        assert!(
matches!0
(
640
2
            prototype.prepare().start("hello", &[]),
641
            Err((super::StartErr::InvalidParameters, _))
642
        ));
643
    }
644
1
}
645
646
#[test]
647
1
fn try_to_call_global() {
648
1
    let module_bytes = wat::parse_str(
649
1
        r#"
650
1
    (module
651
1
        (import "env" "memory" (memory $mem 0 4096))
652
1
        (global $g (export "hello") i32 (i32.const 12))
653
1
    )
654
1
    "#,
655
1
    )
656
1
    .unwrap();
657
658
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
659
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
660
2
            module_bytes: &module_bytes,
661
2
            exec_hint,
662
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
663
2
        })
664
2
        .unwrap();
665
2
        assert!(
matches!0
(
666
2
            prototype.prepare().start("hello", &[]),
667
            Err((super::StartErr::NotAFunction, _))
668
        ));
669
    }
670
1
}
671
672
#[test]
673
1
fn wrong_type_provided_initially() {
674
1
    let module_bytes = wat::parse_str(
675
1
        r#"
676
1
    (module
677
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
678
1
        (import "env" "memory" (memory $mem 8 16))
679
1
        (func (export "hello") (result i32)
680
1
            (call $host_hello (i32.const 3))
681
1
        )
682
1
    )
683
1
    "#,
684
1
    )
685
1
    .unwrap();
686
687
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
688
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
689
2
            module_bytes: &module_bytes,
690
2
            exec_hint,
691
2
            symbols: &mut |_, _, _| Ok(0),
692
2
        })
693
2
        .unwrap();
694
2
695
2
        let mut vm = prototype.prepare().start("hello", &[]).unwrap();
696
2
697
2
        assert!(
matches!0
(
698
2
            vm.run(Some(super::WasmValue::I32(3))),
699
            Err(super::RunErr::BadValueTy { .. })
700
        ));
701
    }
702
1
}
703
704
#[test]
705
1
fn wrong_type_returned_by_host_function_call() {
706
1
    let module_bytes = wat::parse_str(
707
1
        r#"
708
1
    (module
709
1
        (import "host" "hello" (func $host_hello (param i64) (result i32)))
710
1
        (import "env" "memory" (memory $mem 8 16))
711
1
        (func (export "hello") (result i32)
712
1
            (call $host_hello (i64.const 3))
713
1
        )
714
1
    )
715
1
    "#,
716
1
    )
717
1
    .unwrap();
718
719
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
720
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
721
2
            module_bytes: &module_bytes,
722
2
            exec_hint,
723
2
            symbols: &mut |_, _, _| Ok(0),
724
2
        })
725
2
        .unwrap();
726
2
727
2
        let mut vm = prototype.prepare().start("hello", &[]).unwrap();
728
2
729
2
        let Ok(super::ExecOutcome::Interrupted { id: 0, .. }) = vm.run(None) else {
730
0
            panic!()
731
        };
732
2
        assert!(
matches!0
(
733
2
            vm.run(Some(super::WasmValue::I64(3))),
734
            Err(super::RunErr::BadValueTy { .. })
735
        ));
736
    }
737
1
}
738
739
#[test]
740
1
fn memory_min_specified_in_wasm() {
741
1
    let module_bytes = wat::parse_str(
742
1
        r#"
743
1
    (module
744
1
        (import "env" "memory" (memory $mem 16 4096))
745
1
        (func (export "hello") (result i32) i32.const 0)
746
1
    )
747
1
    "#,
748
1
    )
749
1
    .unwrap();
750
751
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
752
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
753
2
            module_bytes: &module_bytes,
754
2
            exec_hint,
755
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
756
2
        })
757
2
        .unwrap();
758
2
        let interpreter = prototype.prepare().start("hello", &[]).unwrap();
759
2
        assert_eq!(interpreter.memory_size(), super::HeapPages::new(16));
760
    }
761
1
}
762
763
#[test]
764
1
fn memory_grow_works() {
765
1
    let module_bytes = wat::parse_str(
766
1
        r#"
767
1
    (module
768
1
        (import "env" "memory" (memory $mem 16 4096))
769
1
        (func (export "hello") (result i32) i32.const 0)
770
1
    )
771
1
    "#,
772
1
    )
773
1
    .unwrap();
774
775
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
776
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
777
2
            module_bytes: &module_bytes,
778
2
            exec_hint,
779
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
780
2
        })
781
2
        .unwrap();
782
2
        let mut interpreter = prototype.prepare().start("hello", &[]).unwrap();
783
2
        assert_eq!(interpreter.memory_size(), super::HeapPages::new(16));
784
2
        interpreter.grow_memory(super::HeapPages::new(3)).unwrap();
785
2
        assert_eq!(interpreter.memory_size(), super::HeapPages::new(19));
786
    }
787
1
}
788
789
#[test]
790
1
fn memory_grow_detects_limit() {
791
1
    let module_bytes = wat::parse_str(
792
1
        r#"
793
1
    (module
794
1
        (import "env" "memory" (memory $mem 16 22))
795
1
        (func (export "hello") (result i32) i32.const 0)
796
1
    )
797
1
    "#,
798
1
    )
799
1
    .unwrap();
800
801
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
802
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
803
2
            module_bytes: &module_bytes,
804
2
            exec_hint,
805
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
806
2
        })
807
2
        .unwrap();
808
2
        let mut interpreter = prototype.prepare().start("hello", &[]).unwrap();
809
2
        assert_eq!(interpreter.memory_size(), super::HeapPages::new(16));
810
2
        assert!(interpreter.grow_memory(super::HeapPages::new(10)).is_err());
811
    }
812
1
}
813
814
#[test]
815
1
fn memory_grow_detects_limit_within_host_function() {
816
1
    let module_bytes = wat::parse_str(
817
1
        r#"
818
1
    (module
819
1
        (import "host" "hello" (func $host_hello (param i32) (result i32)))
820
1
        (import "env" "memory" (memory $mem 8 16))
821
1
        (func (export "hello") (result i32)
822
1
            (call $host_hello (i32.const 3))
823
1
        )
824
1
    )
825
1
    "#,
826
1
    )
827
1
    .unwrap();
828
829
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
830
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
831
2
            module_bytes: &module_bytes,
832
2
            exec_hint,
833
2
            symbols: &mut |_, _, _| Ok(0),
834
2
        })
835
2
        .unwrap();
836
2
837
2
        let mut vm = prototype.prepare().start("hello", &[]).unwrap();
838
2
839
2
        let mut resume_value = None;
840
        loop {
841
4
            match vm.run(resume_value) {
842
                Ok(super::ExecOutcome::Finished {
843
2
                    return_value: Ok(value),
844
2
                }) => {
845
2
                    assert_eq!(value, Some(super::WasmValue::I32(3)));
846
2
                    break;
847
                }
848
                Ok(super::ExecOutcome::Finished {
849
                    return_value: Err(_),
850
0
                }) => panic!(),
851
2
                Ok(super::ExecOutcome::Interrupted { id: 0, params }) => {
852
2
                    assert_eq!(params, vec![super::WasmValue::I32(3)]);
853
2
                    assert!(vm.grow_memory(super::HeapPages::new(12)).is_err());
854
2
                    resume_value = Some(super::WasmValue::I32(3));
855
                }
856
0
                Ok(super::ExecOutcome::Interrupted { .. }) => panic!(),
857
0
                Err(_) => panic!(),
858
            }
859
        }
860
    }
861
1
}
862
863
//  TODO: re-enable this test if the `mutable-globals` feature is enabled
864
/*#[test]
865
fn globals_reinitialized_after_reset() {
866
    let module_bytes = wat::parse_str(
867
        r#"
868
        (module
869
            (import "env" "memory" (memory $mem 8 16))
870
            (global $myglob (export "myglob") (mut i32) (i32.const 5))
871
            (func (export "hello")
872
                global.get $myglob
873
                i32.const 1
874
                i32.add
875
                global.set $myglob)
876
        )
877
        "#,
878
    )
879
    .unwrap();
880
881
    for exec_hint in super::ExecHint::available_engines() {
882
        let mut prototype = super::VirtualMachinePrototype::new(super::Config {
883
            module_bytes: &module_bytes,
884
            exec_hint,
885
            symbols: &mut |_, _, _| Ok(0),
886
        })
887
        .unwrap();
888
        assert_eq!(prototype.global_value("myglob").unwrap(), 5);
889
890
        let mut vm = prototype.prepare().start("hello", &[]).unwrap();
891
        assert!(matches!(
892
            vm.run(None),
893
            Ok(super::ExecOutcome::Finished {
894
                return_value: Ok(None),
895
            })
896
        ));
897
898
        let mut prototype = vm.into_prototype();
899
        assert_eq!(prototype.global_value("myglob").unwrap(), 5);
900
    }
901
}*/
902
903
#[test]
904
1
fn memory_zeroed_after_reset() {
905
1
    let module_bytes = wat::parse_str(
906
1
        r#"
907
1
        (module
908
1
            (import "env" "memory" (memory $mem 1024 4096))
909
1
            (func (export "hello"))
910
1
        )
911
1
        "#,
912
1
    )
913
1
    .unwrap();
914
915
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
916
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
917
2
            module_bytes: &module_bytes,
918
2
            exec_hint,
919
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
920
2
        })
921
2
        .unwrap();
922
2
923
2
        let mut vm = prototype.prepare();
924
2
        vm.write_memory(11, &[5, 6]).unwrap();
925
2
926
2
        let mut vm = vm.start("hello", &[]).unwrap();
927
2
        assert_eq!(vm.read_memory(12, 1).unwrap().as_ref()[0], 6);
928
2
        vm.write_memory(12, &[7]).unwrap();
929
2
        assert_eq!(vm.read_memory(12, 1).unwrap().as_ref()[0], 7);
930
931
2
        assert!(
matches!0
(
932
2
            vm.run(None),
933
            Ok(super::ExecOutcome::Finished {
934
                return_value: Ok(None),
935
            })
936
        ));
937
938
2
        assert_eq!(vm.read_memory(11, 2).unwrap().as_ref(), &[5, 7]);
939
940
2
        let prototype = vm.into_prototype();
941
2
        let vm = prototype.prepare();
942
2
        assert_eq!(vm.read_memory(11, 2).unwrap().as_ref(), &[0, 0]);
943
2
        assert_eq!(vm.read_memory(12, 1).unwrap().as_ref(), &[0]);
944
945
2
        let vm = vm.start("hello", &[]).unwrap();
946
2
        assert_eq!(vm.read_memory(11, 2).unwrap().as_ref(), &[0, 0]);
947
2
        assert_eq!(vm.read_memory(12, 1).unwrap().as_ref(), &[0]);
948
    }
949
1
}
950
951
#[test]
952
1
fn memory_zeroed_after_prepare() {
953
1
    let module_bytes = wat::parse_str(
954
1
        r#"
955
1
        (module
956
1
            (import "env" "memory" (memory $mem 1024 4096))
957
1
            (func (export "hello"))
958
1
        )
959
1
        "#,
960
1
    )
961
1
    .unwrap();
962
963
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
964
2
        let prototype = super::VirtualMachinePrototype::new(super::Config {
965
2
            module_bytes: &module_bytes,
966
2
            exec_hint,
967
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
968
2
        })
969
2
        .unwrap();
970
2
971
2
        let mut vm = prototype.prepare();
972
2
        assert_eq!(vm.read_memory(11, 2).unwrap().as_ref(), &[0, 0]);
973
2
        vm.write_memory(11, &[5, 6]).unwrap();
974
2
975
2
        let vm = vm.into_prototype().prepare();
976
2
        assert_eq!(vm.read_memory(11, 2).unwrap().as_ref(), &[0, 0]);
977
    }
978
1
}
979
980
#[test]
981
1
fn feature_disabled_signext() {
982
1
    let module_bytes = wat::parse_str(
983
1
        r#"
984
1
    (module
985
1
        (import "env" "memory" (memory $mem 0 4096))
986
1
        (func (export "hello") (result i64)
987
1
            (i64.const 2)
988
1
            i64.extend32_s
989
1
        )
990
1
    )
991
1
    "#,
992
1
    )
993
1
    .unwrap();
994
995
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
996
        // TODO: wasmtime doesn't allow disabling sign-ext /!\ test is faulty /!\ figure out what to do
997
        // TODO: see https://github.com/paritytech/substrate/issues/10707#issuecomment-1494081313
998
2
        if Some(exec_hint) == super::ExecHint::force_wasmtime_if_available() {
999
1
            continue;
1000
1
        }
1001
1
1002
1
        assert!(super::VirtualMachinePrototype::new(super::Config {
1003
1
            module_bytes: &module_bytes,
1004
1
            exec_hint,
1005
1
            symbols: &mut |_, _, _| 
Ok(0)0
,
1006
1
        })
1007
1
        .is_err());
1008
    }
1009
1
}
1010
1011
#[test]
1012
1
fn feature_disabled_saturated_float_to_int() {
1013
1
    let module_bytes = wat::parse_str(
1014
1
        r#"
1015
1
    (module
1016
1
        (import "env" "memory" (memory $mem 0 4096))
1017
1
        (func (export "hello") (result i64)
1018
1
            (f32.const 2)
1019
1
            i64.trunc_sat_f32_s
1020
1
        )
1021
1
    )
1022
1
    "#,
1023
1
    )
1024
1
    .unwrap();
1025
1026
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1027
        // TODO: wasmtime doesn't allow disabling this feature /!\ test is faulty /!\ figure out what to do
1028
        // TODO: see https://github.com/paritytech/substrate/issues/10707#issuecomment-1494081313
1029
2
        if Some(exec_hint) == super::ExecHint::force_wasmtime_if_available() {
1030
1
            continue;
1031
1
        }
1032
1
1033
1
        assert!(super::VirtualMachinePrototype::new(super::Config {
1034
1
            module_bytes: &module_bytes,
1035
1
            exec_hint,
1036
1
            symbols: &mut |_, _, _| 
Ok(0)0
,
1037
1
        })
1038
1
        .is_err());
1039
    }
1040
1
}
1041
1042
#[test]
1043
1
fn feature_disabled_threads() {
1044
1
    let module_bytes = wat::parse_str(
1045
1
        r#"
1046
1
    (module
1047
1
        (import "env" "memory" (memory $mem 0 4096))
1048
1
        (func (export "hello") (result i64)
1049
1
            (atomic.fence)
1050
1
            i64.const 2
1051
1
        )
1052
1
    )
1053
1
    "#,
1054
1
    )
1055
1
    .unwrap();
1056
1057
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1058
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
1059
2
            module_bytes: &module_bytes,
1060
2
            exec_hint,
1061
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
1062
2
        })
1063
2
        .is_err());
1064
    }
1065
1
}
1066
1067
#[test]
1068
1
fn feature_disabled_reference_type() {
1069
1
    let module_bytes = wat::parse_str(
1070
1
        r#"
1071
1
    (module
1072
1
        (import "env" "memory" (memory $mem 0 4096))
1073
1
        (func (export "hello") (result externref) unreachable)
1074
1
    )
1075
1
    "#,
1076
1
    )
1077
1
    .unwrap();
1078
1079
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1080
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
1081
2
            module_bytes: &module_bytes,
1082
2
            exec_hint,
1083
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
1084
2
        })
1085
2
        .is_err());
1086
    }
1087
1
}
1088
1089
#[test]
1090
1
fn feature_disabled_bulk_memory() {
1091
1
    let module_bytes = wat::parse_str(
1092
1
        r#"
1093
1
    (module
1094
1
        (import "env" "memory" (memory $mem 0 4096))
1095
1
        (func (export "hello") (result i64)
1096
1
            (memory.fill (i32.const 0) (i32.const 0) (i32.const 0))
1097
1
            i64.const 2
1098
1
        )
1099
1
    )
1100
1
    "#,
1101
1
    )
1102
1
    .unwrap();
1103
1104
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1105
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
1106
2
            module_bytes: &module_bytes,
1107
2
            exec_hint,
1108
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
1109
2
        })
1110
2
        .is_err());
1111
    }
1112
1
}
1113
1114
#[test]
1115
1
fn feature_disabled_multi_value() {
1116
1
    let module_bytes = wat::parse_str(
1117
1
        r#"
1118
1
    (module
1119
1
        (import "env" "memory" (memory $mem 0 4096))
1120
1
        (func (export "hello") (param i64 i64) (result i64 i64 i64)
1121
1
          (local.get 0) (local.get 1) (local.get 0)
1122
1
        )
1123
1
    )
1124
1
    "#,
1125
1
    )
1126
1
    .unwrap();
1127
1128
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1129
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
1130
2
            module_bytes: &module_bytes,
1131
2
            exec_hint,
1132
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
1133
2
        })
1134
2
        .is_err());
1135
    }
1136
1
}
1137
1138
#[test]
1139
1
fn feature_disabled_memory64() {
1140
1
    let module_bytes = wat::parse_str(
1141
1
        r#"
1142
1
    (module
1143
1
        (import "env" "memory" (memory $mem i64 0 4096))
1144
1
    )
1145
1
    "#,
1146
1
    )
1147
1
    .unwrap();
1148
1149
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1150
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
1151
2
            module_bytes: &module_bytes,
1152
2
            exec_hint,
1153
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
1154
2
        })
1155
2
        .is_err());
1156
    }
1157
1
}
1158
1159
#[test]
1160
1
fn feature_disabled_mutable_globals() {
1161
1
    let module_bytes = wat::parse_str(
1162
1
        r#"
1163
1
    (module
1164
1
        (import "env" "memory" (memory $mem 0 4096))
1165
1
        (global $myglob (export "myglob") (mut i32) (i32.const 5))
1166
1
    )
1167
1
    "#,
1168
1
    )
1169
1
    .unwrap();
1170
1171
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1172
        // TODO: wasmtime doesn't allow disabling this feature /!\ test is faulty /!\ figure out what to do
1173
        // TODO: see https://github.com/paritytech/substrate/issues/10707#issuecomment-1494081313
1174
2
        if Some(exec_hint) == super::ExecHint::force_wasmtime_if_available() {
1175
1
            continue;
1176
1
        }
1177
1
1178
1
        assert!(super::VirtualMachinePrototype::new(super::Config {
1179
1
            module_bytes: &module_bytes,
1180
1
            exec_hint,
1181
1
            symbols: &mut |_, _, _| 
Ok(0)0
,
1182
1
        })
1183
1
        .is_err());
1184
    }
1185
1
}
1186
1187
#[test]
1188
1
fn feature_disabled_tail_call() {
1189
1
    let module_bytes = wat::parse_str(
1190
1
        r#"
1191
1
    (module
1192
1
        (import "env" "memory" (memory $mem 0 4096))
1193
1
        (func $fac (param $x i64) (result i64)
1194
1
            (return_call $fac-aux (local.get $x) (i64.const 1))
1195
1
        )
1196
1
        (func $fac-aux (param $x i64) (param $r i64) (result i64)
1197
1
          (if (i64.eqz (local.get $x))
1198
1
            (then (return (local.get $r)))
1199
1
            (else
1200
1
              (return_call $fac-aux
1201
1
                (i64.sub (local.get $x) (i64.const 1))
1202
1
                (i64.mul (local.get $x) (local.get $r))
1203
1
              )
1204
1
            )
1205
1
          )
1206
1
        )
1207
1
    )
1208
1
    "#,
1209
1
    )
1210
1
    .unwrap();
1211
1212
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1213
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
1214
2
            module_bytes: &module_bytes,
1215
2
            exec_hint,
1216
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
1217
2
        })
1218
2
        .is_err());
1219
    }
1220
1
}
1221
1222
#[test]
1223
1
fn feature_disabled_simd128() {
1224
1
    let module_bytes = wat::parse_str(
1225
1
        r#"(module
1226
1
            (type (;0;) (func (param i32) (result i64)))
1227
1
            (type (;1;) (func (param i32 i32 i32)))
1228
1
            (func (;0;) (type 0) (param i32) (result i64)
1229
1
              local.get 0
1230
1
              i64.load)
1231
1
            (func (;1;) (type 1) (param i32 i32 i32)
1232
1
              local.get 0
1233
1
              v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000
1234
1
              local.get 1
1235
1
              i32x4.replace_lane 0
1236
1
              local.get 2
1237
1
              i32x4.replace_lane 2
1238
1
              v128.store)
1239
1
            (func (;2;) (type 0) (param i32) (result i64)
1240
1
              (local i32 i64)
1241
1
              global.get 0
1242
1
              i32.const 16
1243
1
              i32.sub
1244
1
              local.tee 1
1245
1
              global.set 0
1246
1
              local.get 1
1247
1
              local.get 0
1248
1
              local.get 0
1249
1
              call 1
1250
1
              local.get 1
1251
1
              call 0
1252
1
              local.set 2
1253
1
              local.get 1
1254
1
              i32.const 16
1255
1
              i32.add
1256
1
              global.set 0
1257
1
              local.get 2)
1258
1
            (table (;0;) 1 1 funcref)
1259
1
            (memory (;0;) 16)
1260
1
            (global (;0;) (mut i32) (i32.const 1048576))
1261
1
            (global (;1;) i32 (i32.const 1048576))
1262
1
            (global (;2;) i32 (i32.const 1048576))
1263
1
            (export "memory" (memory 0))
1264
1
            (export "test" (func 2))
1265
1
            (export "__data_end" (global 1))
1266
1
            (export "__heap_base" (global 2)))
1267
1
    "#,
1268
1
    )
1269
1
    .unwrap();
1270
1271
3
    for 
exec_hint2
in super::ExecHint::available_engines() {
1272
2
        assert!(super::VirtualMachinePrototype::new(super::Config {
1273
2
            module_bytes: &module_bytes,
1274
2
            exec_hint,
1275
2
            symbols: &mut |_, _, _| 
Ok(0)0
,
1276
2
        })
1277
2
        .is_err());
1278
    }
1279
1
}
1280
1281
// TODO: check that the extended-const feature is disabled: https://github.com/WebAssembly/extended-const/blob/master/proposals/extended-const/Overview.md
1282
1283
// TODO: test for memory reads and writes, including within host functions