A libfuzzer run has discovered some inputs for which the demangler does not terminate.
When minimized, it looks like this: _Zcv1BIRT_EIS1_E
Deciphered:
_Z cv - conversion operator * result type 1B - "B" I - template args begin R - reference type <. T_ - forward template reference | * E - template args end | | | | * parameter type | | I - template args begin | | S1_ - substitution #1 * <' E - template args end
The reason is: template-parameter refs in conversion operator result type create forward-references, while substitutions are instantly resolved via back-references. Together these can create a reference loop. It causes an infinite loop in ReferenceType::collapse().
I see three possible ways to avoid these loops:
- check if resolving a forward reference creates a loop and reject the invalid input (hard to traverse AST at this point)
- check if a substitution contains a malicious forward reference and reject the invalid input (hard to traverse AST at this point; substitutions are quite common: may affect performance; hard to clearly detect loops at this point)
- detect loops in ReferenceType::collapse() (cannot reject the input)
This patch implements (3) as seemingly the least-impact change. As a side effect, such invalid input strings are not rejected and produce garbage, however there are already similar guards in if (Printing) return; checks.
We need to add this at the top of this file:
Otherwise, we'll try to run the test on those configurations and we'll run into the infinite loop. Incidentally, this just stalled the CI for 24 hours (I'll put a timeout on our tests).