When using an LLVM build with exceptions disabled, new will return nullptr when allocation fails. Since new is determined to have nonnull attribute, this null cannot always be caught by the functions that call for allocation (besides for not having this check in any case, for example inside the allocate() function in allocator.h). Functions will just assume getting 0 didn’t happen. The result of this is that the code attempts to write to address 0x0 instead of exiting at any point.
There was a commit in LLVM by Marshal Clow which changes the behavior of __throw_bad_alloc() to abort under exceptions disabled, instead of doing nothing:
However, since operator new never used __throw_bad_alloc() function, rather directly used throw bad_alloc(), this behavior was never added to operator new.
Is there a reason this behavior was retained? Is returing nullptr the correct behavior for operator new when allocation fails? Or is this an oversight? I think the line throw std::bad_alloc() should be replaced with a call to __throw_bad_alloc() to fix this oversight and keep consistent behavior with functions such as copyfmt and do_allocate etc. which use __throw_bad_alloc().
Note: These 2 lines in the 2 operator new functions appear to be the only places in libcxx where an exception is thrown directly, instead of inside a specific __throw_XXX function.
This is the question in a nutshell:
When exceptions are disabled and allocation fails, why does operator new return nullptr instead of aborting?
I think this is an oversight, because every exception thrown (when exceptions are enabled) is thrown through a __throw_XXX function, and those functions all abort when exceptions are disabled. This is important to fix, because currently this causes bad behavior without having any kind of exception, assert, or abort.
Thoughts on this?
See ⚙ D146379 [libc++] These throwing allocation functions shouldn't return null. and Operator new and -fno-exceptions · Issue #60129 · llvm/llvm-project · GitHub. Essentially, this is a known bug and we are (kind-of) working on it. If you want to take a shot and push this forward you’re welcome to.
I think the
operator new is still throwing because the
operator new(std::nothrow) is catching it and returning
Are you working on the issue @philnik posted above (⚙ D146379 [libc++] These throwing allocation functions shouldn't return null.)? I added my opinion here. In summary, I think it’s pretty clear we should just abort in the case that exceptions are disabled and allocation fails. It’s fine that the nothrow version will abort as well in this case. Is there something I can do to help push this forward?
This is on my list. I think it’s an important problem to solve but I also think there may be significant challenges in shipping this since it’s such a fundamental change in a widely used configuration. At the same time, the case in which the failure happens is a fairly extreme one under normal circumstances, but not on all platforms.
I’ve been prevented from looking into this issue for several weeks due to other emergencies (eg CI) but I’ll try to upload a first shot at this today.
Thanks for your patience!