Skip to content

Conversation

khagankhan
Copy link
Contributor

@khagankhan khagankhan commented Sep 4, 2025

Open to review
cc @fitzgen

@khagankhan khagankhan requested a review from a team as a code owner September 4, 2025 09:10
@khagankhan khagankhan requested review from alexcrichton and removed request for a team September 4, 2025 09:10
};

const NUM_PARAMS_RANGE: RangeInclusive<u32> = 0..=10;
const NUM_TYPES_RANGE: RangeInclusive<u32> = 0..=32;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new ranges for Types

vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF],
);

let groups = self.limits.num_rec_groups as usize;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I tried to distribute structs over different recs.

@github-actions github-actions bot added the fuzzing Issues related to our fuzzing infrastructure label Sep 4, 2025
Copy link

github-actions bot commented Sep 4, 2025

Subscribe to Label Action

cc @fitzgen

This issue or pull request has been labeled: "fuzzing"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: fuzzing

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

@alexcrichton alexcrichton requested review from fitzgen and removed request for alexcrichton September 4, 2025 14:30
Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start, although I think we need some tweaks before this can land.

One thing I don't see is any mutators for our types and rec groups (e.g. add/remove types, switch a type from one rec group to another, etc). Is the plan that we will add mutators in follow up PRs? That is fine, but I just want to make sure it isn't overlooked.

Thanks!

Comment on lines 194 to 198
let groups = self.limits.num_rec_groups as usize;
let total = self.limits.num_types as usize;

if groups == 0 {
// Nothing to emit.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of num_{rec_groups,types} can these be max_{rec_groups,types}? We already have an (implicit) count of rec groups and types via the size of the Types::{type_defs,rec_groups} fields. Then we would define a Types::fixup method that takes a reference to the limits and removes any entries beyond the given limit, and finally we would call Types::fixup from TableOps::fixup:

impl Types {
    // ...

    fn fixup(&mut self, limits: &TableOpsLimits) {
        while self.rec_groups.len() > limits.max_rec_groups {
            self.rec_groups.pop_last();
        }
        while self.type_defs.len() > limits.max_types {
            self.type_defs.pop_last();
        }
    }
}

This ensures that we always have a valid number of types and rec groups by the time we get to emitting a Wasm binary, which is similar to how we treat everything else (assume/assert it is valid in to_wasm_binary, enforce validity in fixup).

(Aside: eventually we will probably want custom serialization/deserialization with #[serde(serialize_with = "my_serialization_func")] for all of our container types so that we never try to allocate a huge Vec/BTreeSet/BTreeMap during deserialization of untrusted fuzzer data, leading to OOMs.)

Comment on lines 200 to 202
// Distribute total types across groups: q each, first r get +1.
let q = total / groups;
let r = total % groups;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We specifically do not want to evenly distribute types between rec groups. We want to explore the weird edge cases where all types are in one rec group and all other rec groups are empty, etc. Also, we already have a mapping from type to rec group in the form of the SubType::rec_group field and we should use that mapping, not ignore it.

Instead, I would expect something like this:

// Gather all rec groups and types by rec group.
let mut rec_groups = self.types
                         .rec_groups
                         .iter()
                         .map(|id| (id, vec![]))
                         .collect::<BTreeMap<RecGroupId, Vec<TypeId>>();
for (id, ty) in self.types.type_defs.iter() {
    rec_groups.entry(ty.rec_group).or_default().push(id);
}

// Encode those rec groups and their types into the types section.
for type_ids in rec_groups.values() {
    types.ty().rec(
        type_ids
            .iter()
            .map(|ty_id| todo!("create a `wasm_encoder::SubType` for the type with id `ty_id`"))
            .collect::<Vec<_>>(),
    );
}

Comment on lines 73 to 97
/// Add an empty struct type to a given group
pub fn insert_empty_struct(&mut self, id: TypeId, group: RecGroupId) {
self.type_defs.insert(
id,
SubType {
rec_group: group,
composite_type: CompositeType::Struct(StructType::default()),
},
);
}

/// Return true if there is at least one struct type defined.
pub fn has_structs(&self) -> bool {
self.type_defs
.values()
.any(|t| matches!(t.composite_type, CompositeType::Struct(_)))
}

/// Iterate over the IDs of all struct type definitions.
pub fn struct_ids(&self) -> impl Iterator<Item = &TypeId> {
self.type_defs
.iter()
.filter(|(_, t)| matches!(t.composite_type, CompositeType::Struct(_)))
.map(|(id, _)| id)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of these methods seem to be used?

@khagankhan
Copy link
Contributor Author

Thanks for the comments @fitzgen! Yes mutators are coming next. I just want to go step by step so addressing them is easy

@khagankhan
Copy link
Contributor Author

@fitzgen It is ready for the second round of reviews. There is a failing test of limit > 0 which is related to the #11587 that I am looking at.

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Two tiny nitpicks below, but should be good to merge when they are addressed and the test issue you mentioned is resolved. Thanks!

use super::*;

use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nitpick: newline between imports and declarations

let encode_ty_id = |ty_id: &TypeId| -> wasm_encoder::SubType {
let def = &self.types.type_defs[ty_id];
match &def.composite_type {
CompositeType::Struct(_s) => wasm_encoder::SubType {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: lets exhaustively match on the struct definition here so that as we expand that type we are reminded to update this code by the compiler:

Suggested change
CompositeType::Struct(_s) => wasm_encoder::SubType {
CompositeType::Struct(StructType {}) => wasm_encoder::SubType {

@khagankhan
Copy link
Contributor Author

@fitzgen ready for the review. It passes the tests now. The potential merge should fix #11587

@khagankhan
Copy link
Contributor Author

cc @eeide

@fitzgen fitzgen added this pull request to the merge queue Sep 17, 2025
Merged via the queue into bytecodealliance:main with commit 370042d Sep 17, 2025
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fuzzing Issues related to our fuzzing infrastructure
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants