Skip to content

Commit f0fcd42

Browse files
bormanldionne
authored andcommitted
[libc++abi] Fix possible infinite loop in itanium demangler
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: 1. check if resolving a forward reference creates a loop and reject the invalid input (hard to traverse AST at this point) 2. 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) 3. 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. Fixes https://llvm.org/PR51407 Differential Revision: https://reviews.llvm.org/D107712
1 parent db7c68d commit f0fcd42

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

libcxxabi/src/demangle/ItaniumDemangle.h

+19
Original file line numberDiff line numberDiff line change
@@ -651,15 +651,30 @@ class ReferenceType : public Node {
651651
// Dig through any refs to refs, collapsing the ReferenceTypes as we go. The
652652
// rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any
653653
// other combination collapses to a lvalue ref.
654+
//
655+
// A combination of a TemplateForwardReference and a back-ref Substitution
656+
// from an ill-formed string may have created a cycle; use cycle detection to
657+
// avoid looping forever.
654658
std::pair<ReferenceKind, const Node *> collapse(OutputStream &S) const {
655659
auto SoFar = std::make_pair(RK, Pointee);
660+
// Track the chain of nodes for the Floyd's 'tortoise and hare'
661+
// cycle-detection algorithm, since getSyntaxNode(S) is impure
662+
PODSmallVector<const Node *, 8> Prev;
656663
for (;;) {
657664
const Node *SN = SoFar.second->getSyntaxNode(S);
658665
if (SN->getKind() != KReferenceType)
659666
break;
660667
auto *RT = static_cast<const ReferenceType *>(SN);
661668
SoFar.second = RT->Pointee;
662669
SoFar.first = std::min(SoFar.first, RT->RK);
670+
671+
// The middle of Prev is the 'slow' pointer moving at half speed
672+
Prev.push_back(SoFar.second);
673+
if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) {
674+
// Cycle detected
675+
SoFar.second = nullptr;
676+
break;
677+
}
663678
}
664679
return SoFar;
665680
}
@@ -680,6 +695,8 @@ class ReferenceType : public Node {
680695
return;
681696
SwapAndRestore<bool> SavePrinting(Printing, true);
682697
std::pair<ReferenceKind, const Node *> Collapsed = collapse(s);
698+
if (!Collapsed.second)
699+
return;
683700
Collapsed.second->printLeft(s);
684701
if (Collapsed.second->hasArray(s))
685702
s += " ";
@@ -693,6 +710,8 @@ class ReferenceType : public Node {
693710
return;
694711
SwapAndRestore<bool> SavePrinting(Printing, true);
695712
std::pair<ReferenceKind, const Node *> Collapsed = collapse(s);
713+
if (!Collapsed.second)
714+
return;
696715
if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s))
697716
s += ")";
698717
Collapsed.second->printRight(s);

libcxxabi/test/test_demangle.pass.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
// The demangler does not pass all these tests with the system dylibs on macOS.
1010
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}
1111

12+
// https://llvm.org/PR51407 was not fixed in some previously-released
13+
// demanglers, which causes them to run into the infinite loop.
14+
// UNSUPPORTED: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}
15+
// UNSUPPORTED: use_system_cxx_lib && target={{.+}}-apple-macosx11.0
16+
1217
#include "support/timer.h"
1318
#include <cassert>
1419
#include <cstdio>
@@ -29844,6 +29849,9 @@ const char* cases[][2] =
2984429849

2984529850
{"_ZN3xxx3yyyIvNS_1AILm0EEEZNS_2bb2cc2ddILNS_1eE1EEEvRKNS_1fERKNS_1g1hINS_1iEEERKNS_1jEfRKNS_1kEiPhEUlvE_JEEEvT1_DpT2_", "void xxx::yyy<void, xxx::A<0ul>, void xxx::bb::cc::dd<(xxx::e)1>(xxx::f const&, xxx::g::h<xxx::i> const&, xxx::j const&, float, xxx::k const&, int, unsigned char*)::'lambda'()>(void xxx::bb::cc::dd<(xxx::e)1>(xxx::f const&, xxx::g::h<xxx::i> const&, xxx::j const&, float, xxx::k const&, int, unsigned char*)::'lambda'())"},
2984629851

29852+
// This should be invalid, but it is currently not recognized as such
29853+
// See https://llvm.org/PR51407
29854+
{"_Zcv1BIRT_EIS1_E", "operator B<><>"},
2984729855
};
2984829856

2984929857
const unsigned N = sizeof(cases) / sizeof(cases[0]);

llvm/include/llvm/Demangle/ItaniumDemangle.h

+19
Original file line numberDiff line numberDiff line change
@@ -651,15 +651,30 @@ class ReferenceType : public Node {
651651
// Dig through any refs to refs, collapsing the ReferenceTypes as we go. The
652652
// rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any
653653
// other combination collapses to a lvalue ref.
654+
//
655+
// A combination of a TemplateForwardReference and a back-ref Substitution
656+
// from an ill-formed string may have created a cycle; use cycle detection to
657+
// avoid looping forever.
654658
std::pair<ReferenceKind, const Node *> collapse(OutputStream &S) const {
655659
auto SoFar = std::make_pair(RK, Pointee);
660+
// Track the chain of nodes for the Floyd's 'tortoise and hare'
661+
// cycle-detection algorithm, since getSyntaxNode(S) is impure
662+
PODSmallVector<const Node *, 8> Prev;
656663
for (;;) {
657664
const Node *SN = SoFar.second->getSyntaxNode(S);
658665
if (SN->getKind() != KReferenceType)
659666
break;
660667
auto *RT = static_cast<const ReferenceType *>(SN);
661668
SoFar.second = RT->Pointee;
662669
SoFar.first = std::min(SoFar.first, RT->RK);
670+
671+
// The middle of `Prev` is the 'slow' pointer moving at half speed
672+
Prev.push_back(SoFar.second);
673+
if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) {
674+
// Cycle detected
675+
SoFar.second = nullptr;
676+
break;
677+
}
663678
}
664679
return SoFar;
665680
}
@@ -680,6 +695,8 @@ class ReferenceType : public Node {
680695
return;
681696
SwapAndRestore<bool> SavePrinting(Printing, true);
682697
std::pair<ReferenceKind, const Node *> Collapsed = collapse(s);
698+
if (!Collapsed.second)
699+
return;
683700
Collapsed.second->printLeft(s);
684701
if (Collapsed.second->hasArray(s))
685702
s += " ";
@@ -693,6 +710,8 @@ class ReferenceType : public Node {
693710
return;
694711
SwapAndRestore<bool> SavePrinting(Printing, true);
695712
std::pair<ReferenceKind, const Node *> Collapsed = collapse(s);
713+
if (!Collapsed.second)
714+
return;
696715
if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s))
697716
s += ")";
698717
Collapsed.second->printRight(s);

0 commit comments

Comments
 (0)