[llvm] f6ba985 - [libc++abi] Use std::abort() instead of std::terminate() on failure to allocate

Louis Dionne via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 31 11:58:35 PDT 2023


Author: Louis Dionne
Date: 2023-08-31T14:58:08-04:00
New Revision: f6ba9850c06197e7bb530b9949dddce1e8a169f0

URL: https://github.com/llvm/llvm-project/commit/f6ba9850c06197e7bb530b9949dddce1e8a169f0
DIFF: https://github.com/llvm/llvm-project/commit/f6ba9850c06197e7bb530b9949dddce1e8a169f0.diff

LOG: [libc++abi] Use std::abort() instead of std::terminate() on failure to allocate

Inside the Itanium demangler, we would previously call std::terminate()
after failing to (re)allocate. However, programs are free to install a
custom terminate_handler which does non-trivial things. In fact, by
default libc++abi itself installs a demangling_terminate_handler() which
tries to demangle the currently active exception before aborting the
program.

In case of an out-of-memory exception caused by the system truly having
no more memory (as opposed to attempting to allocate INT_MAX just once),
we will end up trying to demangle the exception, failing to do so because
we can't grow the OutputBuffer inside ItaniumDemangle.h, and then calling
std::terminate() there. That will call the demangling_terminate_handler(),
which will then start this loop again. We eventually end up crashing due
to a stack overflow.

To fix this problem, this patch calls std::abort() directly from the
demangler instead of going through std::terminate(). After all, calling
std::abort() is the default behavior for std::terminate() according
to the Standard, so this behavior is definitely valid. The downside of
this approach is that in case of a "true" out-of-memory condition:
1. the program will throw an exception
2. std::terminate() will be called if uncaught
3. demangling_terminate_handler() will be called and will fail
4. abort() will be called

We'll end up aborting the program without mentioning the cause, which
normally looks like:

  terminating due to uncaught exception of type <TYPE>: <what()-MESSAGE>

Another option would be to properly handle failure-to-allocate inside
ItaniumDemangle.h and to propagate something like an error code or a
std::expected to the caller of all functions in the demangler that
can allocate. Then, we could make sure that __cxa_demangle returns
nullptr when it fails to demangle the input due to any error, as it is
supposed to (but today "true" out-of-memory conditions are not handled
properly). The demangling_terminate_handler() would then see that
__cxa_demangle failed to do its job and would still print the
appropriate message, simply using the non-demangled exception type.
However, this is akin to a partial rewrite of the demangler code since
a large number of functions would now have to return a std::expected
to account for out-of-memory conditions.

Using exceptions would be a lot simpler in terms of code changes and
would achieve the same result, however the demangler can't use exceptions
because it is used inside LLVM and libc++abi implements the exception
runtime anyway (so while it might be possible to use them in that
context, I'd argue we'd only be playing with fire).

rdar://110767664

Differential Revision: https://reviews.llvm.org/D155598

Added: 
    

Modified: 
    libcxxabi/src/demangle/ItaniumDemangle.h
    libcxxabi/src/demangle/Utility.h
    llvm/include/llvm/Demangle/ItaniumDemangle.h
    llvm/include/llvm/Demangle/Utility.h

Removed: 
    


################################################################################
diff  --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index 28562f02144670..c80f343cc876e9 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -61,13 +61,13 @@ template <class T, size_t N> class PODSmallVector {
     if (isInline()) {
       auto *Tmp = static_cast<T *>(std::malloc(NewCap * sizeof(T)));
       if (Tmp == nullptr)
-        std::terminate();
+        std::abort();
       std::copy(First, Last, Tmp);
       First = Tmp;
     } else {
       First = static_cast<T *>(std::realloc(First, NewCap * sizeof(T)));
       if (First == nullptr)
-        std::terminate();
+        std::abort();
     }
     Last = First + S;
     Cap = First + NewCap;

diff  --git a/libcxxabi/src/demangle/Utility.h b/libcxxabi/src/demangle/Utility.h
index 8370633aceba5c..f8a36f88f8e7b0 100644
--- a/libcxxabi/src/demangle/Utility.h
+++ b/libcxxabi/src/demangle/Utility.h
@@ -23,7 +23,6 @@
 #include <cstdint>
 #include <cstdlib>
 #include <cstring>
-#include <exception>
 #include <limits>
 #include <string_view>
 
@@ -49,7 +48,7 @@ class OutputBuffer {
         BufferCapacity = Need;
       Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
       if (Buffer == nullptr)
-        std::terminate();
+        std::abort();
     }
   }
 

diff  --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 550e1699b22841..26acd38f8ae847 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -55,13 +55,13 @@ template <class T, size_t N> class PODSmallVector {
     if (isInline()) {
       auto *Tmp = static_cast<T *>(std::malloc(NewCap * sizeof(T)));
       if (Tmp == nullptr)
-        std::terminate();
+        std::abort();
       std::copy(First, Last, Tmp);
       First = Tmp;
     } else {
       First = static_cast<T *>(std::realloc(First, NewCap * sizeof(T)));
       if (First == nullptr)
-        std::terminate();
+        std::abort();
     }
     Last = First + S;
     Cap = First + NewCap;

diff  --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h
index a906d238cf447b..99ed81461ce413 100644
--- a/llvm/include/llvm/Demangle/Utility.h
+++ b/llvm/include/llvm/Demangle/Utility.h
@@ -23,7 +23,6 @@
 #include <cstdint>
 #include <cstdlib>
 #include <cstring>
-#include <exception>
 #include <limits>
 #include <string_view>
 
@@ -49,7 +48,7 @@ class OutputBuffer {
         BufferCapacity = Need;
       Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
       if (Buffer == nullptr)
-        std::terminate();
+        std::abort();
     }
   }
 


        


More information about the llvm-commits mailing list