1//===-- sanitizer_new_handler.h ---------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file is shared between run-time libraries of sanitizers.
10//
11// It provides the operator new chain-handling framework required by
12// [new.delete.single]/3+/4: a std::get_new_handler() loop plus the
13// throwing / nothrow exhaustion policies that compose with it. Each
14// sanitizer's operator new wrapper supplies two small lambdas:
15//
16// * Alloc — invokes the sanitizer's internal allocator. Returns
17// nullptr, rather than aborting, only when storage cannot
18// be obtained: every nullptr return is treated as a
19// storage failure by the std::get_new_handler() loop.
20// Other detected failures (e.g. invalid alignment) must
21// abort with a diagnostic.
22//
23// * OnExhausted — invokes the sanitizer's "abort with diagnostic"
24// handler (e.g. asan's ReportOutOfMemory + Die()).
25// Required to never return (enforced by UNREACHABLE()).
26//===----------------------------------------------------------------------===//
27#ifndef SANITIZER_NEW_HANDLER_H
28#define SANITIZER_NEW_HANDLER_H
29
30#include "sanitizer_allocator.h"
31#include "sanitizer_internal_defs.h"
32#include "sanitizer_platform.h"
33
34#if !SANITIZER_WINDOWS && defined(__cpp_exceptions)
35# include <new>
36#else
37// Builds without exception support can't include <new>: on Windows the
38// sanitizer runtimes are built without exceptions; elsewhere this TU may
39// have been compiled with -fno-exceptions (e.g. with
40// -DCOMPILER_RT_ASAN_ENABLE_EXCEPTIONS=OFF). Forward-declare just enough
41// of std for the loop; the real std::get_new_handler is supplied by the
42// C++ runtime the sanitizer links against (vcruntime / msvcprt /
43// MinGW libstdc++ / libstdc++ / libc++) — it doesn't need to come from
44// <new>.
45namespace std {
46struct nothrow_t {};
47enum class align_val_t : __sanitizer::usize {};
48using new_handler = void (*)();
49new_handler get_new_handler() noexcept;
50} // namespace std
51#endif // !SANITIZER_WINDOWS && defined(__cpp_exceptions)
52
53namespace __sanitizer {
54
55// Runs std::get_new_handler() per [new.delete.single]/3+/4 until the
56// allocation succeeds (returns the allocated pointer) or the chain is
57// exhausted (returns nullptr).
58//
59// NOTE: Exceptions thrown by Alloc or std::new_handler callbacks escape this
60// function. Callers that need to convert exceptions to a nullptr return
61// (e.g. NewImplNothrow below) must wrap the call in try/catch themselves.
62template <typename Alloc>
63void* RunNewHandlerChain(Alloc alloc) {
64 for (;;) {
65 void* res = alloc();
66 if (LIKELY(res != nullptr))
67 return res;
68 std::new_handler handler = std::get_new_handler();
69 if (!handler)
70 return nullptr;
71 handler();
72 }
73}
74
75// NORETURN wrapper providing pre-C++23 compatibility.
76template <typename OnExhausted>
77NORETURN void InvokeOnExhausted(OnExhausted on_exhausted) {
78 on_exhausted();
79 UNREACHABLE("operator new OnExhausted callable returned");
80}
81
82// Throwing operator new: chain, then on exhaustion throw std::bad_alloc
83// when this TU was compiled with exception support and AllocatorMayReturnNull()
84// is true. Otherwise (Windows runtimes, -fno-exceptions builds, or the
85// default flag value) fall through to the abort path.
86template <typename Alloc, typename OnExhausted>
87void* NewImplThrowing(Alloc alloc, OnExhausted on_exhausted) {
88 void* res = RunNewHandlerChain(alloc);
89 if (LIKELY(res != nullptr))
90 return res;
91#if !SANITIZER_WINDOWS && defined(__cpp_exceptions)
92 if (AllocatorMayReturnNull())
93 throw std::bad_alloc();
94#endif // !SANITIZER_WINDOWS && defined(__cpp_exceptions)
95 InvokeOnExhausted(on_exhausted);
96}
97
98// Nothrow operator new: per [new.delete.single]/4 behaves as-if the
99// throwing form is called within a try/catch. When exception support is
100// absent (Windows runtimes or -fno-exceptions TUs) we can't write the
101// try/catch literally — instead we avoid the throw path entirely, so the
102// main body matches NewImplThrowing with "throw std::bad_alloc()" replaced
103// by "return nullptr".
104template <typename Alloc, typename OnExhausted>
105void* NewImplNothrow(Alloc alloc, OnExhausted on_exhausted) noexcept {
106#if !SANITIZER_WINDOWS && defined(__cpp_exceptions)
107 try {
108#endif // !SANITIZER_WINDOWS && defined(__cpp_exceptions)
109 void* res = RunNewHandlerChain(alloc);
110 if (LIKELY(res != nullptr))
111 return res;
112 if (AllocatorMayReturnNull())
113 return nullptr;
114 InvokeOnExhausted(on_exhausted);
115#if !SANITIZER_WINDOWS && defined(__cpp_exceptions)
116 } catch (...) {
117 return nullptr;
118 }
119#endif // !SANITIZER_WINDOWS && defined(__cpp_exceptions)
120}
121
122} // namespace __sanitizer
123
124#endif // SANITIZER_NEW_HANDLER_H
125