public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null
@ 2022-07-29 16:18 matthijs at stdin dot nl
  2022-07-29 16:18 ` [Bug libstdc++/106477] " matthijs at stdin dot nl
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: matthijs at stdin dot nl @ 2022-07-29 16:18 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

            Bug ID: 106477
           Summary: With -fno-exception operator new(nothrow) aborts
                    instead of returning null
           Product: gcc
           Version: 11.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: matthijs at stdin dot nl
  Target Milestone: ---

The nothrow version of operator new is intended to return null on allocation
failure. However, when libstdc++ is compiled with -fno-exceptions, it aborts
instead.

The cause of this failure is that the nothrow operators work by calling the
regular operators, catching any allocation failure exception and turning that
into a null return. However, with -fno-exceptions, the regular operator aborts
instead of throwing, so the nothrow operator never gets a chance to return
null.

Originally, this *did* work as expected, because the nothrow operators would
just call malloc directly. However, as reported in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68210 this violates the C++11
requirement that the nothrow versions must call the regular versions (so
applications can replace the regular version and get the nothrow for free), so
this was changed in
https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=b66e5a95c0065fda3569a1bfd3766963a848a00d

Note this comment by Jonathan Wakely in the linked report, which actually
already warns against introducing the behavior I am describing (but the comment
was apparently not considered when applying the fix):
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68210#c2

In any case, we have two conflicting requirements:
 1. nothrow operators should return null on failure
 2. nothrow operators should call regular operators

I can see no way to satisfy both. Since -fno-exceptions is already violating
the spec, it would make sense to me to, when -fno-exceptions is specified, only
satisfy 1 and allow 2 to be violated (which is more of a fringe case anyway,
and applications can always replace the nothrow versions too to get the
behavior they need).

Essentially this would mean that with -fno-exceptions, the nothrow versions
would have to call malloc again directly like before (either duplicating code
like before, or maybe introducing a null-returning helper function?).



To reproduce, I made a small testcase. I was originally seeing this in the
Arduino environment on an Atmel samd chip, but I made a self-contained testcase
and tested that using gcc from https://developer.arm.com (using the linker
script from Atmel/Arduino), which is compiled with -fno-exceptions.

The main testcase is simple: An _sbrk() implementation that always fails to
force allocation failure (overriding the default libnosys implementation that
always succeeds), and a single call to operator new that should return null,
but aborts:

$ cat test.cpp 
#include <new>

volatile void* foo;

extern "C"
void *_sbrk(int n) {
  // Just always fail allocation
  return (void*)-1;
}

int main() {
  // This should return nullptr, but actually aborts (with -fno-exceptions)
  foo = new (std::nothrow) char[65000];
  return 0;
}

In addition, I added a minimal startup.c for memory initialization and reset
vector and a linker script taken verbatim from
https://github.com/arduino/ArduinoCore-samd/raw/master/variants/arduino_zero/linker_scripts/gcc/flash_without_bootloader.ld
(I will attach both files next).

Compiled using:

$ ~/Downloads/gcc-arm-11.2-2022.02-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc
-mcpu=cortex-m0plus -mthumb -g -fno-exceptions --specs=nosys.specs
--specs=nano.specs -Tflash_without_bootloader.ld -nostartfiles test.cpp
startup.c -lstdc++

Running this on the Arduino zero (using openocd and gdb to upload the code
through the EDBG port) shows it aborts:

Program received signal SIGINT, Interrupt.
_exit (rc=rc@entry=1) at
/data/jenkins/workspace/GNU-toolchain/arm-11/src/newlib-cygwin/libgloss/libnosys/_exit.c:16
16     
/data/jenkins/workspace/GNU-toolchain/arm-11/src/newlib-cygwin/libgloss/libnosys/_exit.c:
No such file or directory.
(gdb) bt
#0  _exit (rc=rc@entry=1) at
/data/jenkins/workspace/GNU-toolchain/arm-11/src/newlib-cygwin/libgloss/libnosys/_exit.c:16
#1  0x0000013a in abort () at
/data/jenkins/workspace/GNU-toolchain/arm-11/src/newlib-cygwin/newlib/libc/stdlib/abort.c:59
#2  0x00000128 in operator new (sz=65000) at
/data/jenkins/workspace/GNU-toolchain/arm-11/src/gcc/libstdc++-v3/libsupc++/new_op.cc:54
#3  0x00000106 in operator new[] (sz=<optimized out>) at
/data/jenkins/workspace/GNU-toolchain/arm-11/src/gcc/libstdc++-v3/libsupc++/new_opv.cc:32
#4  0x000000fe in operator new[] (sz=<optimized out>) at
/data/jenkins/workspace/GNU-toolchain/arm-11/src/gcc/libstdc++-v3/libsupc++/new_opvnt.cc:38
#5  0x00000034 in main () at test.cpp:17

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
@ 2022-07-29 16:18 ` matthijs at stdin dot nl
  2022-07-29 16:19 ` matthijs at stdin dot nl
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: matthijs at stdin dot nl @ 2022-07-29 16:18 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

--- Comment #1 from Matthijs Kooijman <matthijs at stdin dot nl> ---
Created attachment 53382
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=53382&action=edit
Testcase - main code

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
  2022-07-29 16:18 ` [Bug libstdc++/106477] " matthijs at stdin dot nl
@ 2022-07-29 16:19 ` matthijs at stdin dot nl
  2022-07-29 16:20 ` matthijs at stdin dot nl
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: matthijs at stdin dot nl @ 2022-07-29 16:19 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

--- Comment #2 from Matthijs Kooijman <matthijs at stdin dot nl> ---
Created attachment 53383
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=53383&action=edit
Testcase - startup code

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
  2022-07-29 16:18 ` [Bug libstdc++/106477] " matthijs at stdin dot nl
  2022-07-29 16:19 ` matthijs at stdin dot nl
@ 2022-07-29 16:20 ` matthijs at stdin dot nl
  2022-07-29 16:41 ` redi at gcc dot gnu.org
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: matthijs at stdin dot nl @ 2022-07-29 16:20 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

--- Comment #3 from Matthijs Kooijman <matthijs at stdin dot nl> ---
Created attachment 53384
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=53384&action=edit
Testcase - linker script for ATSAMD21G18 (Arduino zero)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
                   ` (2 preceding siblings ...)
  2022-07-29 16:20 ` matthijs at stdin dot nl
@ 2022-07-29 16:41 ` redi at gcc dot gnu.org
  2022-12-14 13:16 ` redi at gcc dot gnu.org
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: redi at gcc dot gnu.org @ 2022-07-29 16:41 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

Jonathan Wakely <redi at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
   Last reconfirmed|                            |2022-07-29
     Ever confirmed|0                           |1
             Status|UNCONFIRMED                 |NEW
           See Also|                            |https://gcc.gnu.org/bugzill
                   |                            |a/show_bug.cgi?id=68210

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Yes, I've been meaning to revisit this for years. I thought there was another
bug report about it already, but I can't find one now.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
                   ` (3 preceding siblings ...)
  2022-07-29 16:41 ` redi at gcc dot gnu.org
@ 2022-12-14 13:16 ` redi at gcc dot gnu.org
  2023-01-15 18:56 ` matthijs at stdin dot nl
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: redi at gcc dot gnu.org @ 2022-12-14 13:16 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

--- Comment #5 from Jonathan Wakely <redi at gcc dot gnu.org> ---
A partial solution, but needs better preprocessor checks around it:

diff --git a/libstdc++-v3/libsupc++/new_op.cc
b/libstdc++-v3/libsupc++/new_op.cc
index 3a6540a3cd9..b1f31bac7e8 100644
--- a/libstdc++-v3/libsupc++/new_op.cc
+++ b/libstdc++-v3/libsupc++/new_op.cc
@@ -57,3 +57,16 @@ operator new (std::size_t sz) _GLIBCXX_THROW
(std::bad_alloc)

   return p;
 }
+
+#if _GLIBCXX_SHARED && __pic__ && defined _GLIBCXX_MANGLE_SIZE_T \
+  && __has_cpp_attribute(gnu::alias) && __has_cpp_attribute(gnu::visibility)
+namespace __gnu_cxx
+{
+#define STR_(A) #A
+#define STR(A) STR_(A)
+#define OP_NEW "_Znw" STR(_GLIBCXX_MANGLE_SIZE_T)
+
+  [[gnu::alias(OP_NEW), gnu::visibility("hidden")]]
+  void * __default_op_new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc);
+}
+#endif
diff --git a/libstdc++-v3/libsupc++/new_opnt.cc
b/libstdc++-v3/libsupc++/new_opnt.cc
index 3822e04c8cf..23e9981a958 100644
--- a/libstdc++-v3/libsupc++/new_opnt.cc
+++ b/libstdc++-v3/libsupc++/new_opnt.cc
@@ -26,11 +26,43 @@
 #include <bits/exception_defines.h>
 #include "new"

+
+#if _GLIBCXX_SHARED && __pic__
 extern "C" void *malloc (std::size_t);
+namespace __gnu_cxx { void * __default_op_new (std::size_t sz); }
+#endif

 _GLIBCXX_WEAK_DEFINITION void *
 operator new (std::size_t sz, const std::nothrow_t&) noexcept
 {
+  // TODO: Only do this for libstdc++.so not libstdc++.a
+  // TODO: Need proper checks that alias + visibility works correctly.
+#if _GLIBCXX_SHARED && __pic__ && defined _GLIBCXX_MANGLE_SIZE_T \
+  && __has_cpp_attribute(gnu::alias) && __has_cpp_attribute(gnu::visibility)
+  void* (*op_new)(std::size_t) = &::operator new;
+  if (op_new == __gnu_cxx::__default_op_new)
+    {
+      // We know that operator new(size_t) has not been replaced,
+      // and we know that the default version just uses malloc.
+      // So we can implement this directly, without the overhead
+      // of handling bad_alloc exceptions.
+
+      /* malloc (0) is unpredictable; avoid it.  */
+      if (__builtin_expect (sz == 0, false))
+       sz = 1;
+
+      void *p;
+      while ((p = malloc (sz)) == 0)
+       {
+         std::new_handler handler = std::get_new_handler ();
+         if (! handler)
+           return nullptr;
+         handler ();
+       }
+      return p;
+    }
+#endif
+
   // _GLIBCXX_RESOLVE_LIB_DEFECTS
   // 206. operator new(size_t, nothrow) may become unlinked to ordinary
   // operator new if ordinary version replaced

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
                   ` (4 preceding siblings ...)
  2022-12-14 13:16 ` redi at gcc dot gnu.org
@ 2023-01-15 18:56 ` matthijs at stdin dot nl
  2023-01-15 22:53 ` redi at gcc dot gnu.org
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: matthijs at stdin dot nl @ 2023-01-15 18:56 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

--- Comment #6 from Matthijs Kooijman <matthijs at stdin dot nl> ---
Ah, IIUC your patch does not treat -fno-exceptions specially, but just adds a
shortcut for the nothrow new version to skip calling regular new version if it
has not been replaced. In a normal build, that saves throw/catch overhead, and
in a no-exceptions build that prevents the abort associated with that throw.
Clever!

One corner case seems to be when the regular new version is replaced in a
no-exceptions build, but in that case that replacement has no way to signal
failure anyway, and if needed a user can just also replace the nothrow version.

I can't comment on the details of the patch wrt aliases and preprocessor stuff,
but the approach and the gist of the code looks ok to me.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
                   ` (5 preceding siblings ...)
  2023-01-15 18:56 ` matthijs at stdin dot nl
@ 2023-01-15 22:53 ` redi at gcc dot gnu.org
  2023-03-19 21:51 ` nok.raven at gmail dot com
  2023-03-20 10:25 ` redi at gcc dot gnu.org
  8 siblings, 0 replies; 10+ messages in thread
From: redi at gcc dot gnu.org @ 2023-01-15 22:53 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

--- Comment #7 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Matthijs Kooijman from comment #6)
> Ah, IIUC your patch does not treat -fno-exceptions specially, but just adds
> a shortcut for the nothrow new version to skip calling regular new version
> if it has not been replaced. In a normal build, that saves throw/catch
> overhead, and in a no-exceptions build that prevents the abort associated
> with that throw. Clever!

Right.


> One corner case seems to be when the regular new version is replaced in a
> no-exceptions build, but in that case that replacement has no way to signal
> failure anyway, and if needed a user can just also replace the nothrow
> version.

If the regular version has been replaced, we have to call it (and handle
possible exceptions) because we can't know what it does, so calling malloc
directly would not be safe. So yes, users who want to replace new but don't
want exceptions involved in the nothrow new path just need to replace both
forms. That's true already today, and my patch wouldn't change it.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
                   ` (6 preceding siblings ...)
  2023-01-15 22:53 ` redi at gcc dot gnu.org
@ 2023-03-19 21:51 ` nok.raven at gmail dot com
  2023-03-20 10:25 ` redi at gcc dot gnu.org
  8 siblings, 0 replies; 10+ messages in thread
From: nok.raven at gmail dot com @ 2023-03-19 21:51 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

Nikita Kniazev <nok.raven at gmail dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |nok.raven at gmail dot com

--- Comment #8 from Nikita Kniazev <nok.raven at gmail dot com> ---
bug 93016 might be related?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bug libstdc++/106477] With -fno-exception operator new(nothrow) aborts instead of returning null
  2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
                   ` (7 preceding siblings ...)
  2023-03-19 21:51 ` nok.raven at gmail dot com
@ 2023-03-20 10:25 ` redi at gcc dot gnu.org
  8 siblings, 0 replies; 10+ messages in thread
From: redi at gcc dot gnu.org @ 2023-03-20 10:25 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106477

--- Comment #9 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Nikita Kniazev from comment #8)
> bug 93016 might be related?

Not really. This is about the behaviour of the library's operator new. In 93016
the compiler should never even call the library function, because the
new-expression is erroneous.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2023-03-20 10:25 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-29 16:18 [Bug libstdc++/106477] New: With -fno-exception operator new(nothrow) aborts instead of returning null matthijs at stdin dot nl
2022-07-29 16:18 ` [Bug libstdc++/106477] " matthijs at stdin dot nl
2022-07-29 16:19 ` matthijs at stdin dot nl
2022-07-29 16:20 ` matthijs at stdin dot nl
2022-07-29 16:41 ` redi at gcc dot gnu.org
2022-12-14 13:16 ` redi at gcc dot gnu.org
2023-01-15 18:56 ` matthijs at stdin dot nl
2023-01-15 22:53 ` redi at gcc dot gnu.org
2023-03-19 21:51 ` nok.raven at gmail dot com
2023-03-20 10:25 ` redi at gcc dot gnu.org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).