jiang1997 opened PR #11837 from jiang1997:structnew-limit-guard to bytecodealliance:main:
libFuzzer can mutate serialized bytes to create states where operations reference non-existent resources (LocalGet with num_params=0, etc). This caused panics when fixup() tried to normalize indices via modulo.
Fix by replacing these operations with equivalent ones before fixup():
- LocalGet/GlobalGet → Null (when params/globals = 0)
- LocalSet/GlobalSet → Drop (when params/globals = 0)
- StructNew → Drop (when max_types = 0)
This preserves stack effects and ensures generated Wasm is always valid.
<!--
Please make sure you include the following information:
If this work has been discussed elsewhere, please include a link to that
conversation. If it was discussed in an issue, just mention "issue #...".Explain why this change is needed. If the details are in an issue already,
this can be brief.Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.htmlPlease ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->
jiang1997 requested fitzgen for a review on PR #11837.
jiang1997 requested wasmtime-fuzz-reviewers for a review on PR #11837.
jiang1997 updated PR #11837.
github-actions[bot] commented on PR #11837:
Subscribe to Label Action
cc @fitzgen
<details>
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 <code>.github/subscribe-to-label.json</code> configuration file.
Learn more.
</details>
fitzgen submitted PR review:
Thanks! A couple things to address below before we merge this.
fitzgen created PR review comment:
Because this is inside a submodule of the module where
TableOps::fixupis defined, we can just call it directly, instead of doing it indirectly viato_wasm_binaryand then ignoring the resulting Wasm binary:ops.fixup();
fitzgen created PR review comment:
If we are going to attempt to replace invalid ops with new ones that have the same type signature (i.e. pop the same operand types from the stack and then push the same type of results) then we should actually do that for
StructNew: insertfields.len()dropinstructions (currently always 0 since the struct support is still WIP) and then aref.null structinstruction to produce the ~same result type (will be abstract instead of concrete, but this is the closest we can get without defining concrete types).But I could see this code becoming a little bit of a hassle/burden as time goes on and we extend this fuzz target to cover all of the GC proposal. So another option would be to simply continue to the next iteration of the loop in these cases, and let each
op.fixup(...)call deal with the fallout of the changes to the operand stack (since they have to do that anyways due to random mutations to the op sequence).
fitzgen commented on PR #11837:
cc @khagankhan
jiang1997 updated PR #11837.
jiang1997 edited PR #11837:
Fix by filtering out problematic operations before fixup() runs:
- Skip StructNew when max_types = 0
- Skip LocalGet/LocalSet when num_params = 0
- Skip GlobalGet/GlobalSet when num_globals = 0
This prevents the division by zero and ensures generated Wasm is valid.
jiang1997 requested fitzgen for a review on PR #11837.
fitzgen submitted PR review:
Thanks!
Last updated: Dec 06 2025 at 07:03 UTC