Skip to content

Commit 6470aa9

Browse files
authored
wasm-smith: Implement support for generating GC instructions (#1382)
* wasm-smith: Implement support for generating GC instructions Found and fixed a bunch of bugs in the validator, encoder, and the generator itself. Since then, this has been fuzzing for about 18 hours without finding any new bugs. * review feedback
1 parent 5d42986 commit 6470aa9

File tree

8 files changed

+2644
-770
lines changed

8 files changed

+2644
-770
lines changed

crates/fuzz-stats/src/bin/failed-instantiations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl State {
103103
config.allow_start_export = false;
104104

105105
// Wasmtime doesn't support this proposal yet.
106-
config.tail_call_enabled = false;
106+
config.gc_enabled = false;
107107

108108
let mut wasm = wasm_smith::Module::new(config, &mut u)?;
109109
wasm.ensure_termination(10_000);

crates/wasm-encoder/src/core/code.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,8 +1614,8 @@ impl Encode for Instruction<'_> {
16141614
sink.push(0x18);
16151615
let cast_flags =
16161616
(from_ref_type.nullable as u8) | ((to_ref_type.nullable as u8) << 1);
1617-
relative_depth.encode(sink);
16181617
sink.push(cast_flags);
1618+
relative_depth.encode(sink);
16191619
from_ref_type.heap_type.encode(sink);
16201620
to_ref_type.heap_type.encode(sink);
16211621
}
@@ -1628,8 +1628,8 @@ impl Encode for Instruction<'_> {
16281628
sink.push(0x19);
16291629
let cast_flags =
16301630
(from_ref_type.nullable as u8) | ((to_ref_type.nullable as u8) << 1);
1631-
relative_depth.encode(sink);
16321631
sink.push(cast_flags);
1632+
relative_depth.encode(sink);
16331633
from_ref_type.heap_type.encode(sink);
16341634
to_ref_type.heap_type.encode(sink);
16351635
}

crates/wasm-encoder/src/core/types.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl TryFrom<wasmparser::FuncType> for FuncType {
102102
}
103103

104104
/// Represents a type of an array in a WebAssembly module.
105-
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
105+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
106106
pub struct ArrayType(pub FieldType);
107107

108108
#[cfg(feature = "wasmparser")]
@@ -178,6 +178,21 @@ impl TryFrom<wasmparser::StorageType> for StorageType {
178178
}
179179
}
180180

181+
impl StorageType {
182+
/// Is this storage type defaultable?
183+
pub fn is_defaultable(&self) -> bool {
184+
self.unpack().is_defaultable()
185+
}
186+
187+
/// Unpack this storage type into a value type.
188+
pub fn unpack(&self) -> ValType {
189+
match self {
190+
StorageType::I8 | StorageType::I16 => ValType::I32,
191+
StorageType::Val(v) => *v,
192+
}
193+
}
194+
}
195+
181196
/// The type of a core WebAssembly value.
182197
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
183198
pub enum ValType {
@@ -216,6 +231,32 @@ impl TryFrom<wasmparser::ValType> for ValType {
216231
}
217232
}
218233

234+
impl ValType {
235+
/// Is this a numeric value type?
236+
pub fn is_numeric(&self) -> bool {
237+
match self {
238+
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
239+
ValType::V128 | ValType::Ref(_) => false,
240+
}
241+
}
242+
243+
/// Is this a vector type?
244+
pub fn is_vector(&self) -> bool {
245+
match self {
246+
ValType::V128 => true,
247+
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::Ref(_) => false,
248+
}
249+
}
250+
251+
/// Is this a reference type?
252+
pub fn is_reference(&self) -> bool {
253+
match self {
254+
ValType::Ref(_) => true,
255+
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
256+
}
257+
}
258+
}
259+
219260
impl FuncType {
220261
/// Creates a new [`FuncType`] from the given `params` and `results`.
221262
pub fn new<P, R>(params: P, results: R) -> Self
@@ -257,6 +298,14 @@ impl ValType {
257298
pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF);
258299
/// Alias for the `exnref` type in WebAssembly
259300
pub const EXNREF: ValType = ValType::Ref(RefType::EXNREF);
301+
302+
/// Is this value defaultable?
303+
pub fn is_defaultable(&self) -> bool {
304+
match self {
305+
ValType::Ref(r) => r.nullable,
306+
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => true,
307+
}
308+
}
260309
}
261310

262311
impl Encode for StorageType {

crates/wasm-smith/src/core.rs

Lines changed: 178 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,50 @@ pub(crate) struct SubType {
249249
pub(crate) composite_type: CompositeType,
250250
}
251251

252+
impl SubType {
253+
fn unwrap_struct(&self) -> &StructType {
254+
self.composite_type.unwrap_struct()
255+
}
256+
257+
fn unwrap_func(&self) -> &Rc<FuncType> {
258+
self.composite_type.unwrap_func()
259+
}
260+
261+
fn unwrap_array(&self) -> &ArrayType {
262+
self.composite_type.unwrap_array()
263+
}
264+
}
265+
252266
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
253267
pub(crate) enum CompositeType {
254268
Array(ArrayType),
255269
Func(Rc<FuncType>),
256270
Struct(StructType),
257271
}
258272

273+
impl CompositeType {
274+
fn unwrap_struct(&self) -> &StructType {
275+
match self {
276+
CompositeType::Struct(s) => s,
277+
_ => panic!("not a struct"),
278+
}
279+
}
280+
281+
fn unwrap_func(&self) -> &Rc<FuncType> {
282+
match self {
283+
CompositeType::Func(f) => f,
284+
_ => panic!("not a func"),
285+
}
286+
}
287+
288+
fn unwrap_array(&self) -> &ArrayType {
289+
match self {
290+
CompositeType::Array(a) => a,
291+
_ => panic!("not an array"),
292+
}
293+
}
294+
}
295+
259296
/// A function signature.
260297
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
261298
pub(crate) struct FuncType {
@@ -392,6 +429,96 @@ impl Module {
392429
Ok(())
393430
}
394431

432+
#[inline]
433+
fn val_type_is_sub_type(&self, a: ValType, b: ValType) -> bool {
434+
match (a, b) {
435+
(a, b) if a == b => true,
436+
(ValType::Ref(a), ValType::Ref(b)) => self.ref_type_is_sub_type(a, b),
437+
_ => false,
438+
}
439+
}
440+
441+
/// Is `a` a subtype of `b`?
442+
fn ref_type_is_sub_type(&self, a: RefType, b: RefType) -> bool {
443+
if a == b {
444+
return true;
445+
}
446+
447+
if a.nullable && !b.nullable {
448+
return false;
449+
}
450+
451+
self.heap_type_is_sub_type(a.heap_type, b.heap_type)
452+
}
453+
454+
fn heap_type_is_sub_type(&self, a: HeapType, b: HeapType) -> bool {
455+
use HeapType as HT;
456+
match (a, b) {
457+
(a, b) if a == b => true,
458+
459+
(HT::Eq | HT::I31 | HT::Struct | HT::Array | HT::None, HT::Any) => true,
460+
(HT::I31 | HT::Struct | HT::Array | HT::None, HT::Eq) => true,
461+
(HT::NoExtern, HT::Extern) => true,
462+
(HT::NoFunc, HT::Func) => true,
463+
(HT::None, HT::I31 | HT::Array | HT::Struct) => true,
464+
465+
(HT::Concrete(a), HT::Eq | HT::Any) => matches!(
466+
self.ty(a).composite_type,
467+
CompositeType::Array(_) | CompositeType::Struct(_)
468+
),
469+
470+
(HT::Concrete(a), HT::Struct) => {
471+
matches!(self.ty(a).composite_type, CompositeType::Struct(_))
472+
}
473+
474+
(HT::Concrete(a), HT::Array) => {
475+
matches!(self.ty(a).composite_type, CompositeType::Array(_))
476+
}
477+
478+
(HT::Concrete(a), HT::Func) => {
479+
matches!(self.ty(a).composite_type, CompositeType::Func(_))
480+
}
481+
482+
(HT::Concrete(mut a), HT::Concrete(b)) => loop {
483+
if a == b {
484+
return true;
485+
}
486+
if let Some(supertype) = self.ty(a).supertype {
487+
a = supertype;
488+
} else {
489+
return false;
490+
}
491+
},
492+
493+
(HT::None, HT::Concrete(b)) => matches!(
494+
self.ty(b).composite_type,
495+
CompositeType::Array(_) | CompositeType::Struct(_)
496+
),
497+
498+
(HT::NoFunc, HT::Concrete(b)) => {
499+
matches!(self.ty(b).composite_type, CompositeType::Func(_))
500+
}
501+
502+
// Nothing else matches. (Avoid full wildcard matches so that
503+
// adding/modifying variants is easier in the future.)
504+
(HT::Concrete(_), _)
505+
| (HT::Func, _)
506+
| (HT::Extern, _)
507+
| (HT::Any, _)
508+
| (HT::None, _)
509+
| (HT::NoExtern, _)
510+
| (HT::NoFunc, _)
511+
| (HT::Eq, _)
512+
| (HT::Struct, _)
513+
| (HT::Array, _)
514+
| (HT::I31, _) => false,
515+
516+
// TODO: `exn` probably will be its own type hierarchy and will
517+
// probably get `noexn` as well.
518+
(HT::Exn, _) => false,
519+
}
520+
}
521+
395522
fn arbitrary_types(&mut self, u: &mut Unstructured) -> Result<()> {
396523
assert!(self.config.min_types <= self.config.max_types);
397524
while self.types.len() < self.config.min_types {
@@ -601,28 +728,17 @@ impl Module {
601728
}
602729
}
603730

604-
fn arbitrary_matching_ref_type(
605-
&mut self,
606-
u: &mut Unstructured,
607-
ty: RefType,
608-
) -> Result<RefType> {
731+
fn arbitrary_matching_ref_type(&self, u: &mut Unstructured, ty: RefType) -> Result<RefType> {
609732
Ok(RefType {
610-
// TODO: For now, only create allow nullable reference
611-
// types. Eventually we should support non-nullable reference types,
612-
// but this means that we will also need to recognize when it is
613-
// impossible to create an instance of the reference (eg `(ref
614-
// nofunc)` has no instances, and self-referential types that
615-
// contain a non-null self-reference are also impossible to create).
616-
nullable: true,
733+
nullable: ty.nullable,
617734
heap_type: self.arbitrary_matching_heap_type(u, ty.heap_type)?,
618735
})
619736
}
620737

621-
fn arbitrary_matching_heap_type(
622-
&mut self,
623-
u: &mut Unstructured,
624-
ty: HeapType,
625-
) -> Result<HeapType> {
738+
fn arbitrary_matching_heap_type(&self, u: &mut Unstructured, ty: HeapType) -> Result<HeapType> {
739+
if !self.config.gc_enabled {
740+
return Ok(ty);
741+
}
626742
use HeapType as HT;
627743
let mut choices = vec![ty];
628744
match ty {
@@ -716,7 +832,7 @@ impl Module {
716832
}
717833

718834
fn arbitrary_super_type_of_ref_type(
719-
&mut self,
835+
&self,
720836
u: &mut Unstructured,
721837
ty: RefType,
722838
) -> Result<RefType> {
@@ -733,10 +849,13 @@ impl Module {
733849
}
734850

735851
fn arbitrary_super_type_of_heap_type(
736-
&mut self,
852+
&self,
737853
u: &mut Unstructured,
738854
ty: HeapType,
739855
) -> Result<HeapType> {
856+
if !self.config.gc_enabled {
857+
return Ok(ty);
858+
}
740859
use HeapType as HT;
741860
let mut choices = vec![ty];
742861
match ty {
@@ -860,6 +979,45 @@ impl Module {
860979
}
861980
}
862981

982+
fn arbitrary_ref_type(&self, u: &mut Unstructured) -> Result<RefType> {
983+
Ok(RefType {
984+
nullable: true,
985+
heap_type: self.arbitrary_heap_type(u)?,
986+
})
987+
}
988+
989+
fn arbitrary_heap_type(&self, u: &mut Unstructured) -> Result<HeapType> {
990+
assert!(self.config.reference_types_enabled);
991+
992+
if self.config.gc_enabled && !self.types.is_empty() && u.arbitrary()? {
993+
let type_ref_limit = u32::try_from(self.types.len()).unwrap();
994+
let idx = u.int_in_range(0..=type_ref_limit)?;
995+
return Ok(HeapType::Concrete(idx));
996+
}
997+
998+
let mut choices = vec![HeapType::Func, HeapType::Extern];
999+
if self.config.exceptions_enabled {
1000+
choices.push(HeapType::Exn);
1001+
}
1002+
if self.config.gc_enabled {
1003+
choices.extend(
1004+
[
1005+
HeapType::Any,
1006+
HeapType::None,
1007+
HeapType::NoExtern,
1008+
HeapType::NoFunc,
1009+
HeapType::Eq,
1010+
HeapType::Struct,
1011+
HeapType::Array,
1012+
HeapType::I31,
1013+
]
1014+
.iter()
1015+
.copied(),
1016+
);
1017+
}
1018+
u.choose(&choices).copied()
1019+
}
1020+
8631021
fn arbitrary_func_type(
8641022
&mut self,
8651023
u: &mut Unstructured,
@@ -2212,6 +2370,7 @@ flags! {
22122370
Table,
22132371
Memory,
22142372
Control,
2373+
Aggregate,
22152374
}
22162375
}
22172376

0 commit comments

Comments
 (0)