From 00841c51e15470219fb7de225dcfda2d9ba58082 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 5 Mar 2026 18:34:41 -0800 Subject: [PATCH] Tweak heap type fuzzer for JS prototypes Make the fuzzer more likely to produce descriptor fields that can be used to configure JS prototypes. This will help the main fuzzer, which uses types generated by the heap type fuzzer, to find bugs where configured prototypes are incorrectly removed. --- src/tools/fuzzing/heap-types.cpp | 19 +++++-- test/lit/fuzz-types.test | 96 +++++++++++++++----------------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 3bc74eb0f1a..af8e235e442 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -19,7 +19,6 @@ #include "ir/gc-type-utils.h" #include "ir/subtypes.h" -#include "support/insert_ordered.h" #include "tools/fuzzing.h" #include "tools/fuzzing/heap-types.h" @@ -275,12 +274,13 @@ struct HeapTypeGeneratorImpl { builder[index].setOpen(subtypeIndices[index].size() > 1 || rand.oneIn(2)); auto kind = typeKinds[index]; auto share = HeapType(builder[index]).getShared(); + bool isDesc = describedIndices[index].has_value(); if (!supertypeIndices[index]) { // No nontrivial supertype, so create a root type. if (std::get_if(&kind)) { builder[index] = generateSignature(); } else if (std::get_if(&kind)) { - builder[index] = generateStruct(share); + builder[index] = generateStruct(share, isDesc); } else if (std::get_if(&kind)) { builder[index] = generateArray(share); } else { @@ -415,7 +415,13 @@ struct HeapTypeGeneratorImpl { return {params, generateReturnType()}; } - Field generateField(Shareability share) { + Field generateField(Shareability share, bool isPrototypeField = false) { + // If this field could configure a prototype, then we want to give it a type + // that lets it do so a significant portion of the time. + if (isPrototypeField && share == Unshared && rand.oneIn(2)) { + auto nullability = rand.oneIn(2) ? NonNullable : Nullable; + return {Type(HeapType::ext, nullability), Immutable}; + } auto mutability = rand.oneIn(2) ? Mutable : Immutable; if (rand.oneIn(6)) { return {rand.oneIn(2) ? Field::i8 : Field::i16, mutability}; @@ -424,10 +430,13 @@ struct HeapTypeGeneratorImpl { } } - Struct generateStruct(Shareability share) { + Struct generateStruct(Shareability share, bool isDesc) { std::vector fields(rand.upTo(params.MAX_STRUCT_SIZE + 1)); + // Prototypes are configured on the first field of descriptors types. + bool isPrototypeField = isDesc; for (auto& field : fields) { - field = generateField(share); + field = generateField(share, isPrototypeField); + isPrototypeField = false; } return {fields}; } diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test index 840fc1bb36b..44d475f6e3d 100644 --- a/test/lit/fuzz-types.test +++ b/test/lit/fuzz-types.test @@ -1,64 +1,56 @@ -;; RUN: wasm-fuzz-types -v --seed=0 | filecheck %s +;; RUN: wasm-fuzz-types -v --seed=2 | filecheck %s -;; CHECK: Running with seed 0 +;; CHECK: Running with seed 2 ;; CHECK-NEXT: Built 20 types: -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $0 (sub (shared (array i8)))) -;; CHECK-NEXT: (type $1 (shared (array (ref $1)))) -;; CHECK-NEXT: (type $2 (sub (shared (func (param i64 (ref null $5) i64) (result (ref $2)))))) -;; CHECK-NEXT: (type $3 (sub $0 (shared (array i8)))) -;; CHECK-NEXT: (type $4 (sub (array (ref null $9)))) -;; CHECK-NEXT: (type $5 (sub final $0 (shared (array i8)))) -;; CHECK-NEXT: (type $6 (sub $2 (shared (func (param i64 (ref null (shared eq)) i64) (result (ref $8)))))) -;; CHECK-NEXT: (type $7 (sub (shared (descriptor $9) (struct (field (ref null $7)) (field (mut i64)) (field (mut i8)) (field i32) (field (mut i32)))))) -;; CHECK-NEXT: (type $8 (sub $2 (shared (func (param i64 (ref null $0) i64) (result (ref $8)))))) -;; CHECK-NEXT: (type $9 (shared (describes $7) (descriptor $12) (struct (field i32) (field (mut (ref $8))) (field f64)))) -;; CHECK-NEXT: (type $10 (sub (array (mut externref)))) -;; CHECK-NEXT: (type $11 (sub $4 (array (ref $9)))) -;; CHECK-NEXT: (type $12 (sub (shared (describes $9) (struct (field (mut f64)) (field (mut i32)) (field (mut f64)))))) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $13 (sub (struct (field (mut i16)) (field i64) (field (mut (ref (shared func))))))) -;; CHECK-NEXT: (type $14 (sub (shared (array (ref null $14))))) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $15 (shared (array (ref null (shared extern))))) -;; CHECK-NEXT: (type $16 (sub final $14 (shared (array (ref null $16))))) +;; CHECK-NEXT: (type $0 (shared (struct))) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $1 (array (ref $2))) +;; CHECK-NEXT: (type $2 (sub (shared (array (mut i16))))) +;; CHECK-NEXT: (type $3 (sub (shared (array i32)))) +;; CHECK-NEXT: (type $4 (sub (descriptor $5) (struct (field (mut (ref $0)))))) +;; CHECK-NEXT: (type $5 (sub (describes $4) (struct (field f64) (field (mut i64))))) +;; CHECK-NEXT: (type $6 (sub (array v128))) +;; CHECK-NEXT: (type $7 (shared (struct (field f32) (field (mut (ref $0)))))) +;; CHECK-NEXT: (type $8 (sub (shared (struct (field f64) (field (mut (ref (shared struct)))) (field (mut f64)) (field i16) (field i32) (field i64))))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $17 (shared (struct (field (mut (ref null $2))) (field i32) (field (mut (ref $14)))))) -;; CHECK-NEXT: (type $18 (sub $11 (array (ref $9)))) -;; CHECK-NEXT: (type $19 (sub $2 (shared (func (param i64 (ref null $0) i64) (result (ref $6)))))) +;; CHECK-NEXT: (type $9 (descriptor $12) (struct (field i64) (field i16))) +;; CHECK-NEXT: (type $10 (array (mut (ref null $5)))) +;; CHECK-NEXT: (type $11 (sub (shared (func (param (ref $7) f64 (ref $9)) (result (ref null $10)))))) +;; CHECK-NEXT: (type $12 (sub (describes $9) (descriptor $13) (struct (field (ref (shared any))) (field (mut (ref extern))) (field v128) (field (ref null $17))))) +;; CHECK-NEXT: (type $13 (sub (describes $12) (descriptor $17) (struct (field externref) (field (mut i8)) (field (mut i32)) (field (mut f32)) (field i16) (field (mut (ref null $6)))))) +;; CHECK-NEXT: (type $14 (sub (func (result i64)))) +;; CHECK-NEXT: (type $15 (sub (shared (func)))) +;; CHECK-NEXT: (type $16 (shared (func (result (ref null $0))))) +;; CHECK-NEXT: (type $17 (sub (describes $13) (struct (field (ref extern))))) +;; CHECK-NEXT: (type $18 (sub (func (param v128 (ref null $10))))) +;; CHECK-NEXT: (type $19 (sub final $11 (shared (func (param (ref null (shared any)) f64 (ref any)) (result (ref $10)))))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ;; CHECK-NEXT: Inhabitable types: ;; CHECK-NEXT: ;; CHECK-NEXT: Built 20 types: -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $0 (sub (shared (array i8)))) -;; CHECK-NEXT: (type $1 (shared (array (ref null $1)))) -;; CHECK-NEXT: (type $2 (sub (shared (func (param i64 (ref null $5) i64) (result (ref $2)))))) -;; CHECK-NEXT: (type $3 (sub $0 (shared (array i8)))) -;; CHECK-NEXT: (type $4 (sub (array (ref null $9)))) -;; CHECK-NEXT: (type $5 (sub final $0 (shared (array i8)))) -;; CHECK-NEXT: (type $6 (sub $2 (shared (func (param i64 (ref null (shared eq)) i64) (result (ref $8)))))) -;; CHECK-NEXT: (type $7 (sub (shared (descriptor $9) (struct (field (ref null $7)) (field (mut i64)) (field (mut i8)) (field i32) (field (mut i32)))))) -;; CHECK-NEXT: (type $8 (sub $2 (shared (func (param i64 (ref null $0) i64) (result (ref $8)))))) -;; CHECK-NEXT: (type $9 (shared (describes $7) (descriptor $12) (struct (field i32) (field (mut (ref $8))) (field f64)))) -;; CHECK-NEXT: (type $10 (sub (array (mut externref)))) -;; CHECK-NEXT: (type $11 (sub $4 (array (ref $9)))) -;; CHECK-NEXT: (type $12 (sub (shared (describes $9) (struct (field (mut f64)) (field (mut i32)) (field (mut f64)))))) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $13 (sub (struct (field (mut i16)) (field i64) (field (mut (ref (shared func))))))) -;; CHECK-NEXT: (type $14 (sub (shared (array (ref null $14))))) +;; CHECK-NEXT: (type $0 (shared (struct))) +;; CHECK-NEXT: (rec +;; CHECK-NEXT: (type $1 (array (ref $2))) +;; CHECK-NEXT: (type $2 (sub (shared (array (mut i16))))) +;; CHECK-NEXT: (type $3 (sub (shared (array i32)))) +;; CHECK-NEXT: (type $4 (sub (descriptor $5) (struct (field (mut (ref $0)))))) +;; CHECK-NEXT: (type $5 (sub (describes $4) (struct (field f64) (field (mut i64))))) +;; CHECK-NEXT: (type $6 (sub (array v128))) +;; CHECK-NEXT: (type $7 (shared (struct (field f32) (field (mut (ref $0)))))) +;; CHECK-NEXT: (type $8 (sub (shared (struct (field f64) (field (mut (ref (shared struct)))) (field (mut f64)) (field i16) (field i32) (field i64))))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $15 (shared (array (ref null (shared extern))))) -;; CHECK-NEXT: (type $16 (sub final $14 (shared (array (ref null $16))))) +;; CHECK-NEXT: (type $9 (descriptor $12) (struct (field i64) (field i16))) +;; CHECK-NEXT: (type $10 (array (mut (ref null $5)))) +;; CHECK-NEXT: (type $11 (sub (shared (func (param (ref $7) f64 (ref $9)) (result (ref null $10)))))) +;; CHECK-NEXT: (type $12 (sub (describes $9) (descriptor $13) (struct (field (ref (shared any))) (field (mut externref)) (field v128) (field (ref null $17))))) +;; CHECK-NEXT: (type $13 (sub (describes $12) (descriptor $17) (struct (field externref) (field (mut i8)) (field (mut i32)) (field (mut f32)) (field i16) (field (mut (ref null $6)))))) +;; CHECK-NEXT: (type $14 (sub (func (result i64)))) +;; CHECK-NEXT: (type $15 (sub (shared (func)))) +;; CHECK-NEXT: (type $16 (shared (func (result (ref null $0))))) +;; CHECK-NEXT: (type $17 (sub (describes $13) (struct (field externref)))) +;; CHECK-NEXT: (type $18 (sub (func (param v128 (ref null $10))))) +;; CHECK-NEXT: (type $19 (sub final $11 (shared (func (param (ref null (shared any)) f64 (ref any)) (result (ref $10)))))) ;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $17 (shared (struct (field (mut (ref null $2))) (field i32) (field (mut (ref $14)))))) -;; CHECK-NEXT: (type $18 (sub $11 (array (ref $9)))) -;; CHECK-NEXT: (type $19 (sub $2 (shared (func (param i64 (ref null $0) i64) (result (ref $6)))))) -;; CHECK-NEXT: ) \ No newline at end of file