Repository navigation
Conversation
`operator<<(std::ostream&, Literal)` instantiated a `PrintLimiter` (incrementing `PrintLimiter::printed`), but never checked `limiter.stop()`, so would infinitely recurse when printing cyclic data. This was a regression from #9051. Check `limiter.stop()` at the entry of `operator<<(std::ostream&, Literal)` to print `[..]` and return early when the print limit is reached, and break out of the `GCData` element loop once `limiter.stop()` becomes true. Add tests for wasm-ctor-eval and --fuzz-exec showing that we no longer infinitely recurse.
There was a problem hiding this comment.
Code Review
This pull request introduces a print limiter check in src/wasm/literal.cpp to prevent infinite recursion and excessive output when formatting and printing cyclic or recursive Garbage Collection (GC) structures. It also adds test cases in test/lit/ctor-eval/gc-cycle.wast and test/lit/exec/gc-cycle-leak.wast to verify that printing cyclic structures terminates correctly and outputs the expected truncated format ([..]). No review comments were provided, and there is no feedback to address.
| } | ||
| o << literal.getElement(i); | ||
| if (limiter.stop()) { | ||
| break; |
There was a problem hiding this comment.
Should we print [..], to indicate we stopped early?
There was a problem hiding this comment.
Ah, actually this ends up printing very many [..], specifically one for each level of nesting that gets unwound after the limit is reached. A single [..] will already have been printed by https://github.com/WebAssembly/binaryen/pull/9305/changes#diff-d063145a0a2ebd6ba09876c06a240d79e15c376eeee0c56bf69d10453fffc9b9R733 after the limit is first reached, so I think that's enough.
There was a problem hiding this comment.
Concretely, here's the updated test output after this change:
+ ;; CHECK-NEXT: [LoggingExternalInterface log-execution [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref
(type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0)))))
[ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0
))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $str
uct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null
$struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref
null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut
(ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field
(mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (
field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (str
uct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0
(struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $str
uct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type
$struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref
(type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0)))))
[ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0)
)))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $stru
ct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null
$struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref
null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut
(ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field
(mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (f
ield (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (stru
ct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0
(struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $stru
ct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type
$struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (
type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [
ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))
))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struc
t.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $
struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref n
ull $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (
ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (mut (ref null $struct.0))))) [ref (type $struct.0 (struct (field (
mut (ref null $struct.0))))) [..][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]]
[..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..
]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]][..]]]
I don't think all the extra [..] at the end are helpful.
operator<<(std::ostream&, Literal)instantiated aPrintLimiter(incrementingPrintLimiter::printed), but never checkedlimiter.stop(), so would infinitely recurse when printing cyclic data. This was a regression from #9051.Check
limiter.stop()at the entry ofoperator<<(std::ostream&, Literal)to print[..]and return early when the print limit is reached, and break out of theGCDataelement loop oncelimiter.stop()becomes true.Add tests for wasm-ctor-eval and --fuzz-exec showing that we no longer infinitely recurse.