This is an archive of the discontinued LLVM Phabricator instance.

Fix possible infinite loop in itanium demangler
ClosedPublic

Authored by borman on Aug 8 2021, 3:43 AM.

Details

Summary

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://bugs.llvm.org/show_bug.cgi?id=51407

Diff Detail

Event Timeline

borman requested review of this revision.Aug 8 2021, 3:43 AM
borman created this revision.
Herald added a reviewer: Restricted Project. · View Herald TranscriptAug 8 2021, 3:43 AM
Herald added a project: Restricted Project. · View Herald Transcript
borman added inline comments.Aug 8 2021, 3:48 AM
llvm/include/llvm/Demangle/ItaniumDemangle.h
112–229

I could submit this change as a NFC to clean up the actual patch; however the move itself would be out of context.

borman edited the summary of this revision. (Show Details)Aug 8 2021, 3:51 AM
ldionne requested changes to this revision.Aug 9 2021, 6:37 AM
ldionne added a subscriber: ldionne.

Thanks a lot for looking into this! When you re-upload the patch, can you please do it with arc diff so that context is included in the diff? See https://www.llvm.org/docs/Phabricator.html#requesting-a-review-via-the-command-line for instructions.

libcxxabi/test/test_demangle.pass.cpp
29847

We need to add this at the top of this file:

// https://llvm.org/PR51407 was not fixed in some previously-released demanglers, which
// causes them to run into the infinite loop.
// UNSUPPORTED: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}
// UNSUPPORTED: use_system_cxx_lib && target={{.+}}-apple-macosx11.0

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).

29848

Please normalize to https://llvm.org/PR51407.

llvm/include/llvm/Demangle/ItaniumDemangle.h
112–229

I would rather the move be performed as a separate NFC. The commit message can mention it's for a follow-up change.

This revision now requires changes to proceed.Aug 9 2021, 6:37 AM
borman added inline comments.Aug 9 2021, 10:28 AM
llvm/include/llvm/Demangle/ItaniumDemangle.h
112–229

Separate review for the move: https://reviews.llvm.org/D107712

borman updated this revision to Diff 366895.Aug 17 2021, 7:38 AM

rebase onto D107771, fix issues

borman updated this revision to Diff 366896.Aug 17 2021, 7:45 AM

resubmit diff against main

borman updated this revision to Diff 366897.Aug 17 2021, 7:48 AM

resubmit diff against D107771

Unfortunately, Arcanist doesn't give me any options to send test_demangler.pass.cpp diff as text:

Diff for 'libcxxabi/test/test_demangle.pass.cpp' with context is
5,403,433 bytes in length. Generally, source changes should not be this
large. If the file is not a text file, you can mark it 'binary'. Mark
this file as 'binary' and continue? [y/N] y

Seems like enormous strings in test cases upset it and I'm not proficient enough with it to bypass this.

The actual diff is:

diff --git a/libcxxabi/test/test_demangle.pass.cpp b/libcxxabi/test/test_demangle.pass.cpp
index 1780e684cbb5..0744172a6cbb 100644
--- a/libcxxabi/test/test_demangle.pass.cpp
+++ b/libcxxabi/test/test_demangle.pass.cpp
@@ -9,6 +9,11 @@
 // The demangler does not pass all these tests with the system dylibs on macOS.
 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}

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

     {"_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'())"},

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

 const unsigned N = sizeof(cases) / sizeof(cases[0]);
borman updated this revision to Diff 366899.Aug 17 2021, 7:55 AM

upload with reduced diff context

borman updated this revision to Diff 366901.Aug 17 2021, 7:56 AM

upload with increased diff context

borman marked 2 inline comments as done.Aug 17 2021, 7:57 AM
ldionne accepted this revision.Aug 17 2021, 3:08 PM

This fell under my radar. I'm fine with this. I'll commit it for you, thanks!

This revision is now accepted and ready to land.Aug 17 2021, 3:08 PM

Oh, and thanks a lot for the great explanation of the problem and the fix, I really appreciate that.

This revision was automatically updated to reflect the committed changes.