cfallin opened PR #12333 from cfallin:icmp-recursion to bytecodealliance:main:
We had a set of rules introduced in #11097 that attempted to optimize the case of testing the result of an
icmpfor a nonzero value. This allowed optimization of, for example,(((x == 0) == 0) == 0 ...)to a single level, eitherx == 0orx != 0depending on even/odd nesting depth.Unfortunately this kind of recursion in the backend has a depth bounded only by the user input, hence creates a DoS vulnerability: the wrong kind of compiler input can cause a stack overflow in Cranelift at compilation time. This case is reachable from Wasmtime's Wasm frontend via the
i32.eqzoperator (for example) as well.Ideally, this kind of deep rewrite is best done in our mid-end optimizer, where we think carefully about bounds for recursive rewrites. The left-hand sides for the backend rules should really be fixed shapes that correspond to machine instructions, rather than ad-hoc peephole optimizations in their own right.
This fix thus simply removes the recursion case that causes the blowup. The patch includes two tests: one with optimizations disabled, showing correct compilation (without the fix, this case fails to compile with a stack overflow), and one with optimizations enabled, showing that the mid-end properly cleans up the nested expression and we get the expected one-level result anyway.
Note: this was reported as a security issue by @venkkatesh-sekar (thanks!); per our security policy, compilation DoS is not covered, so this is being fixed in a public PR. I will subsequently make a patch release to our currently supported versions (v36, v39, v40, 41).
<!--
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
-->
cfallin requested fitzgen for a review on PR #12333.
cfallin requested wasmtime-compiler-reviewers for a review on PR #12333.
cfallin requested alexcrichton for a review on PR #12333.
cfallin edited PR #12333:
We had a set of rules introduced in #11097 that attempted to optimize the case of testing the result of an
icmpfor a nonzero value. This allowed optimization of, for example,(((x == 0) == 0) == 0 ...)to a single level, eitherx == 0orx != 0depending on even/odd nesting depth.Unfortunately this kind of recursion in the backend has a depth bounded only by the user input, hence creates a compiler DoS vulnerability: the wrong kind of compiler input can cause a stack overflow in Cranelift at compilation time. This case is reachable from Wasmtime's Wasm frontend via the
i32.eqzoperator (for example) as well.Ideally, this kind of deep rewrite is best done in our mid-end optimizer, where we think carefully about bounds for recursive rewrites. The left-hand sides for the backend rules should really be fixed shapes that correspond to machine instructions, rather than ad-hoc peephole optimizations in their own right.
This fix thus simply removes the recursion case that causes the blowup. The patch includes two tests: one with optimizations disabled, showing correct compilation (without the fix, this case fails to compile with a stack overflow), and one with optimizations enabled, showing that the mid-end properly cleans up the nested expression and we get the expected one-level result anyway.
Note: this was reported as a security issue by @venkkatesh-sekar (thanks!); per our security policy, compilation DoS is not covered, so this is being fixed in a public PR. I will subsequently make a patch release to our currently supported versions (v36, v39, v40, 41).
<!--
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
-->
fitzgen submitted PR review.
cfallin has enabled auto merge for PR #12333.
alexcrichton commented on PR #12333:
I haven't dug deeply into the fallout here, but it looks like just removing these rules isn't enough to retain the codegen we have from before. A rough hunch is that there's a number of entrypoints for using these ISLE helpers and while the wasm test case here is one which is also cleaned up by the optimizer I think that these rules might be load-bearing for other codegen patterns unrelated to the optimizer.
It might need to be the case that some entrypoints use one rule, which includes these deleted rules, but then these rules don't recurse on themselves?
alexcrichton commented on PR #12333:
Although, from another angle, this seems fine as a thing to backport since it's clearly such low impact. I'm not aware of these codegen patterns being load bearing in terms of performance so a temporary minor regression while a "full fix" is developed for
mainI think would also be reasonable. In such a case though there's a number of codegen/etc tests to update to get CI passing.
cfallin commented on PR #12333:
This broke some existing lowerings; playing a bit more with various ways to break the cycle.
cfallin commented on PR #12333:
(On refresh, what Alex said -- somehow those comments didn't appear for me.)
cfallin updated PR #12333.
cfallin commented on PR #12333:
OK, I've reworked things a bit to preserve exactly the same codegen on branches -- I effectively peeled one layer off of
is_nonzero_cmpto continue to use with branches but the previous backedge fromemit_cmptois_nonzero_cmpnow invokesis_nonzerowhich has only the base cases. Together with explicit LHS patterns to peel one level ofuextendthis leaves us with no test-output changes now.
cfallin updated PR #12333.
cfallin requested wasmtime-core-reviewers for a review on PR #12333.
cfallin commented on PR #12333:
Or, well, spoke too soon -- no Cranelift filetest changes. A few disas tests that do FP compares do regress to materializing the bool.
cfallin updated PR #12333.
cfallin commented on PR #12333:
OK, I've gotten full equivalence on the disas tests too -- this needed one new mid-end rule to see through a pattern (
fcmp(...) != 0) that we were previously only ever simplifying with this peephole rule in the backend.@fitzgen mind giving this another look, since it's a little heavier than before?
Re: backport, I could certainly backport the rule deletions only, but I'd be a little concerned about performance regressions in that case because this affects many branches. If we feel ok about these rewrites I think the full patch should be applicable back to v36 (LTS).
alexcrichton submitted PR review.
alexcrichton created PR review comment:
I think this change, the
uextend-no-longer-recurses, is what's causing the remaining disassembly differences. Locally I'm seeing(uextend (vall_{any,true} ..))I believe for example.Do you think it'd be worth adding those specifically here as well? I'm not sure how to deduplicate this amongst the
is_nonzeroselectors above otherwise. Although nowadays egraphs might be able to rewrite(brif (uextend val))to(brif val)which I know historically was difficult
alexcrichton commented on PR #12333:
Also, logistically, @cfallin are you ok running point on backporting/point releases? And 24.0.0, while supported, is unaffected right?
cfallin commented on PR #12333:
Yes, I'm happy to do the backports too. (Sorry, very slow today taking scraps of time between meetings and other interrupts; can hopefully do backports by end of tomorrow at latest.)
alexcrichton commented on PR #12333:
Oh no worries! Just want to make sure there's explicitly an owner. Tomorrow is totally fine and I can be around to hit approvals and help babysit CI. We've had some CI changes recently like trusted publishing which raises the risk of broken CI on other branches, so something to watch out for.
cfallin updated PR #12333.
cfallin submitted PR review.
cfallin created PR review comment:
Ah, I missed this -- fixed in latest push (which drops the commit with any diffs to tests at all).
cfallin created PR review comment:
(Fixed by adding explicit patterns in
is_nonzeroas you suggest)
cfallin submitted PR review.
cfallin commented on PR #12333:
OK, this should be good to go -- if r+ here then I can put up backports momentarily.
cfallin edited a comment on PR #12333:
OK, this should be good to go -- if r+ here (on new changes) then I can put up backports momentarily.
alexcrichton submitted PR review.
cfallin has enabled auto merge for PR #12333.
cfallin merged PR #12333.
adambratschikaye commented on PR #12333:
Thanks for taking care of this @cfallin, @alexcrichton, and @fitzgen!
Last updated: Jan 29 2026 at 13:25 UTC