Skip to content

Commit c2cdafa

Browse files
committed
[libc++] Pass type information down to __libcpp_allocate
Currently, places where we call __libcpp_allocate must drop type information on the ground even when they actually have such information available. That is unfortunate since some toolchains and system allocators are able to provide improved security when they know what type is being allocated. This is the purpose of http://wg21.link/p2719, where we introduce a new variant of `operator new` which takes a type in its interface. A different but related issue is that `std::allocator` does not honor any in-class `T::operator new` since it is specified to call the global `::operator new` instead. This patch closes the gap to make it trivial for implementations that provide typed memory allocators to actually benefit from that information in more contexts, and also makes libc++ forward-compatible with future proposals that would fix the existing defects in `std::allocator`. Since this is a widely-used function and making this a template could have an impact on debug info sizes, I tried minimizing the number of templated layers by removing `__do_deallocate_handle_size`, which was easy to replace with a macro (and IMO this leads to cleaner code). We could also explore using `_LIBCPP_NODEBUG` on `__libcpp_allocate` and friends if that proves to be a problem.
1 parent 0964328 commit c2cdafa

File tree

7 files changed

+58
-50
lines changed

7 files changed

+58
-50
lines changed

libcxx/include/__memory/allocator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::v
100100
if (__libcpp_is_constant_evaluated()) {
101101
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
102102
} else {
103-
return static_cast<_Tp*>(std::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp)));
103+
return static_cast<_Tp*>(std::__libcpp_allocate<_Tp>(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp)));
104104
}
105105
}
106106

@@ -115,7 +115,7 @@ class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::v
115115
if (__libcpp_is_constant_evaluated()) {
116116
::operator delete(__p);
117117
} else {
118-
std::__libcpp_deallocate((void*)__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));
118+
std::__libcpp_deallocate<_Tp>((void*)__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));
119119
}
120120
}
121121

libcxx/include/__memory/builtin_new_allocator.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2323
// deallocating memory using __builtin_operator_new and
2424
// __builtin_operator_delete. It should be used in preference to
2525
// `std::allocator<T>` to avoid additional instantiations.
26+
//
27+
// TODO: Get rid of this class since it's only used in std::function and
28+
// we use __allocate_type there anyway.
2629
struct __builtin_new_allocator {
2730
struct __builtin_new_deleter {
2831
typedef void* pointer_type;
@@ -31,7 +34,7 @@ struct __builtin_new_allocator {
3134
: __size_(__size), __align_(__align) {}
3235

3336
_LIBCPP_HIDE_FROM_ABI void operator()(void* __p) const _NOEXCEPT {
34-
std::__libcpp_deallocate(__p, __size_, __align_);
37+
std::__libcpp_deallocate<char>(__p, __size_, __align_);
3538
}
3639

3740
private:
@@ -42,22 +45,24 @@ struct __builtin_new_allocator {
4245
typedef unique_ptr<void, __builtin_new_deleter> __holder_t;
4346

4447
_LIBCPP_HIDE_FROM_ABI static __holder_t __allocate_bytes(size_t __s, size_t __align) {
45-
return __holder_t(std::__libcpp_allocate(__s, __align), __builtin_new_deleter(__s, __align));
48+
return __holder_t(std::__libcpp_allocate<char>(__s, __align), __builtin_new_deleter(__s, __align));
4649
}
4750

4851
_LIBCPP_HIDE_FROM_ABI static void __deallocate_bytes(void* __p, size_t __s, size_t __align) _NOEXCEPT {
49-
std::__libcpp_deallocate(__p, __s, __align);
52+
std::__libcpp_deallocate<char>(__p, __s, __align);
5053
}
5154

5255
template <class _Tp>
5356
_LIBCPP_NODEBUG _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI static __holder_t __allocate_type(size_t __n) {
54-
return __allocate_bytes(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));
57+
auto const __size = __n * sizeof(_Tp);
58+
auto const __align = _LIBCPP_ALIGNOF(_Tp);
59+
return __holder_t(std::__libcpp_allocate<_Tp>(__size, __align), __builtin_new_deleter(__size, __align));
5560
}
5661

5762
template <class _Tp>
5863
_LIBCPP_NODEBUG _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI static void
5964
__deallocate_type(void* __p, size_t __n) _NOEXCEPT {
60-
__deallocate_bytes(__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));
65+
std::__libcpp_deallocate<_Tp>(__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));
6166
}
6267
};
6368

libcxx/include/__memory/unique_temporary_buffer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct __temporary_buffer_deleter {
3939
return;
4040
}
4141

42-
std::__libcpp_deallocate_unsized((void*)__ptr, _LIBCPP_ALIGNOF(_Tp));
42+
std::__libcpp_deallocate_unsized<_Tp>((void*)__ptr, _LIBCPP_ALIGNOF(_Tp));
4343
}
4444
};
4545

libcxx/include/__utility/small_buffer.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class __small_buffer {
6666
if constexpr (__fits_in_buffer<_Stored>) {
6767
return std::launder(reinterpret_cast<_Stored*>(__buffer_));
6868
} else {
69-
byte* __allocation = static_cast<byte*>(std::__libcpp_allocate(sizeof(_Stored), alignof(_Stored)));
69+
byte* __allocation = static_cast<byte*>(std::__libcpp_allocate<_Stored>(sizeof(_Stored), alignof(_Stored)));
7070
std::construct_at(reinterpret_cast<byte**>(__buffer_), __allocation);
7171
return std::launder(reinterpret_cast<_Stored*>(__allocation));
7272
}
@@ -75,7 +75,7 @@ class __small_buffer {
7575
template <class _Stored>
7676
_LIBCPP_HIDE_FROM_ABI void __dealloc() noexcept {
7777
if constexpr (!__fits_in_buffer<_Stored>)
78-
std::__libcpp_deallocate(*reinterpret_cast<void**>(__buffer_), sizeof(_Stored), alignof(_Stored));
78+
std::__libcpp_deallocate<_Stored>(*reinterpret_cast<void**>(__buffer_), sizeof(_Stored), alignof(_Stored));
7979
}
8080

8181
template <class _Stored, class... _Args>

libcxx/include/new

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ _LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI bool __is_overaligned_for_new(siz
271271
#endif
272272
}
273273

274-
template <class... _Args>
274+
template <class _Tp, class... _Args>
275275
_LIBCPP_HIDE_FROM_ABI void* __libcpp_operator_new(_Args... __args) {
276276
#if __has_builtin(__builtin_operator_new) && __has_builtin(__builtin_operator_delete)
277277
return __builtin_operator_new(__args...);
@@ -280,7 +280,7 @@ _LIBCPP_HIDE_FROM_ABI void* __libcpp_operator_new(_Args... __args) {
280280
#endif
281281
}
282282

283-
template <class... _Args>
283+
template <class _Tp, class... _Args>
284284
_LIBCPP_HIDE_FROM_ABI void __libcpp_operator_delete(_Args... __args) _NOEXCEPT {
285285
#if __has_builtin(__builtin_operator_new) && __has_builtin(__builtin_operator_delete)
286286
__builtin_operator_delete(__args...);
@@ -289,52 +289,54 @@ _LIBCPP_HIDE_FROM_ABI void __libcpp_operator_delete(_Args... __args) _NOEXCEPT {
289289
#endif
290290
}
291291

292+
template <class _Tp>
292293
inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_allocate(size_t __size, size_t __align) {
293294
#if _LIBCPP_HAS_ALIGNED_ALLOCATION
294295
if (__is_overaligned_for_new(__align)) {
295296
const align_val_t __align_val = static_cast<align_val_t>(__align);
296-
return __libcpp_operator_new(__size, __align_val);
297+
return std::__libcpp_operator_new<_Tp>(__size, __align_val);
297298
}
298299
#endif
299300

300301
(void)__align;
301-
return __libcpp_operator_new(__size);
302+
return std::__libcpp_operator_new<_Tp>(__size);
302303
}
303304

304-
template <class... _Args>
305-
_LIBCPP_HIDE_FROM_ABI void __do_deallocate_handle_size(void* __ptr, size_t __size, _Args... __args) _NOEXCEPT {
306-
#if !_LIBCPP_HAS_SIZED_DEALLOCATION
307-
(void)__size;
308-
return std::__libcpp_operator_delete(__ptr, __args...);
305+
#if _LIBCPP_HAS_SIZED_DEALLOCATION
306+
# define _LIBCPP_ONLY_IF_SIZED_DEALLOCATION(...) __VA_ARGS__
309307
#else
310-
return std::__libcpp_operator_delete(__ptr, __size, __args...);
308+
# define _LIBCPP_ONLY_IF_SIZED_DEALLOCATION(...) /* nothing */
311309
#endif
312-
}
313310

311+
template <class _Tp>
314312
inline _LIBCPP_HIDE_FROM_ABI void __libcpp_deallocate(void* __ptr, size_t __size, size_t __align) _NOEXCEPT {
313+
(void)__size;
315314
#if !_LIBCPP_HAS_ALIGNED_ALLOCATION
316315
(void)__align;
317-
return __do_deallocate_handle_size(__ptr, __size);
316+
return std::__libcpp_operator_delete<_Tp>(__ptr _LIBCPP_ONLY_IF_SIZED_DEALLOCATION(, __size));
318317
#else
319318
if (__is_overaligned_for_new(__align)) {
320319
const align_val_t __align_val = static_cast<align_val_t>(__align);
321-
return __do_deallocate_handle_size(__ptr, __size, __align_val);
320+
return std::__libcpp_operator_delete<_Tp>(__ptr _LIBCPP_ONLY_IF_SIZED_DEALLOCATION(, __size), __align_val);
322321
} else {
323-
return __do_deallocate_handle_size(__ptr, __size);
322+
return std::__libcpp_operator_delete<_Tp>(__ptr _LIBCPP_ONLY_IF_SIZED_DEALLOCATION(, __size));
324323
}
325324
#endif
326325
}
327326

327+
#undef _LIBCPP_ONLY_IF_SIZED_DEALLOCATION
328+
329+
template <class _Tp>
328330
inline _LIBCPP_HIDE_FROM_ABI void __libcpp_deallocate_unsized(void* __ptr, size_t __align) _NOEXCEPT {
329331
#if !_LIBCPP_HAS_ALIGNED_ALLOCATION
330332
(void)__align;
331-
return __libcpp_operator_delete(__ptr);
333+
return std::__libcpp_operator_delete<_Tp>(__ptr);
332334
#else
333335
if (__is_overaligned_for_new(__align)) {
334336
const align_val_t __align_val = static_cast<align_val_t>(__align);
335-
return __libcpp_operator_delete(__ptr, __align_val);
337+
return std::__libcpp_operator_delete<_Tp>(__ptr, __align_val);
336338
} else {
337-
return __libcpp_operator_delete(__ptr);
339+
return std::__libcpp_operator_delete<_Tp>(__ptr);
338340
}
339341
#endif
340342
}

libcxx/src/memory_resource.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,22 @@ static bool is_aligned_to(void* ptr, size_t align) {
4141
class _LIBCPP_EXPORTED_FROM_ABI __new_delete_memory_resource_imp : public memory_resource {
4242
void* do_allocate(size_t bytes, size_t align) override {
4343
#if _LIBCPP_HAS_ALIGNED_ALLOCATION
44-
return std::__libcpp_allocate(bytes, align);
44+
return std::__libcpp_allocate<std::byte>(bytes, align);
4545
#else
4646
if (bytes == 0)
4747
bytes = 1;
48-
void* result = std::__libcpp_allocate(bytes, align);
48+
void* result = std::__libcpp_allocate<std::byte>(bytes, align);
4949
if (!is_aligned_to(result, align)) {
50-
std::__libcpp_deallocate(result, bytes, align);
50+
std::__libcpp_deallocate<std::byte>(result, bytes, align);
5151
__throw_bad_alloc();
5252
}
5353
return result;
5454
#endif
5555
}
5656

57-
void do_deallocate(void* p, size_t bytes, size_t align) override { std::__libcpp_deallocate(p, bytes, align); }
57+
void do_deallocate(void* p, size_t bytes, size_t align) override {
58+
std::__libcpp_deallocate<std::byte>(p, bytes, align);
59+
}
5860

5961
bool do_is_equal(const memory_resource& other) const noexcept override { return &other == this; }
6062
};

libcxx/test/libcxx/language.support/support.dynamic/libcpp_deallocate.sh.cpp

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -94,34 +94,34 @@ struct alloc_stats {
9494
};
9595
alloc_stats stats;
9696

97-
void operator delete(void* p)TEST_NOEXCEPT {
97+
void operator delete(void* p) TEST_NOEXCEPT {
9898
::free(p);
9999
stats.plain_called++;
100100
stats.last_size = stats.last_align = -1;
101101
}
102102

103103
#ifndef NO_SIZE
104-
void operator delete(void* p, std::size_t n)TEST_NOEXCEPT {
104+
void operator delete(void* p, std::size_t n) TEST_NOEXCEPT {
105105
::free(p);
106106
stats.sized_called++;
107-
stats.last_size = n;
107+
stats.last_size = n;
108108
stats.last_align = -1;
109109
}
110110
#endif
111111

112112
#ifndef NO_ALIGN
113-
void operator delete(void* p, std::align_val_t a)TEST_NOEXCEPT {
113+
void operator delete(void* p, std::align_val_t a) TEST_NOEXCEPT {
114114
std::__libcpp_aligned_free(p);
115115
stats.aligned_called++;
116116
stats.last_align = static_cast<int>(a);
117-
stats.last_size = -1;
117+
stats.last_size = -1;
118118
}
119119

120-
void operator delete(void* p, std::size_t n, std::align_val_t a)TEST_NOEXCEPT {
120+
void operator delete(void* p, std::size_t n, std::align_val_t a) TEST_NOEXCEPT {
121121
std::__libcpp_aligned_free(p);
122122
stats.aligned_sized_called++;
123123
stats.last_align = static_cast<int>(a);
124-
stats.last_size = n;
124+
stats.last_size = n;
125125
}
126126
#endif
127127

@@ -133,45 +133,45 @@ void test_libcpp_dealloc() {
133133
std::size_t over_align_val = TEST_ALIGNOF(std::max_align_t) * 2;
134134
#endif
135135
std::size_t under_align_val = TEST_ALIGNOF(int);
136-
std::size_t with_size_val = 2;
136+
std::size_t with_size_val = 2;
137137

138138
{
139-
std::__libcpp_deallocate_unsized(p, under_align_val);
139+
std::__libcpp_deallocate_unsized<char>(p, under_align_val);
140140
assert(stats.expect_plain());
141141
}
142142
stats.reset();
143143

144144
#if defined(NO_SIZE) && defined(NO_ALIGN)
145145
{
146-
std::__libcpp_deallocate(p, with_size_val, over_align_val);
146+
std::__libcpp_deallocate<char>(p, with_size_val, over_align_val);
147147
assert(stats.expect_plain());
148148
}
149149
stats.reset();
150150
#elif defined(NO_SIZE)
151151
{
152-
std::__libcpp_deallocate(p, with_size_val, over_align_val);
152+
std::__libcpp_deallocate<char>(p, with_size_val, over_align_val);
153153
assert(stats.expect_align(over_align_val));
154154
}
155155
stats.reset();
156156
#elif defined(NO_ALIGN)
157157
{
158-
std::__libcpp_deallocate(p, with_size_val, over_align_val);
158+
std::__libcpp_deallocate<char>(p, with_size_val, over_align_val);
159159
assert(stats.expect_size(with_size_val));
160160
}
161161
stats.reset();
162162
#else
163163
{
164-
std::__libcpp_deallocate(p, with_size_val, over_align_val);
164+
std::__libcpp_deallocate<char>(p, with_size_val, over_align_val);
165165
assert(stats.expect_size_align(with_size_val, over_align_val));
166166
}
167167
stats.reset();
168168
{
169-
std::__libcpp_deallocate_unsized(p, over_align_val);
169+
std::__libcpp_deallocate_unsized<char>(p, over_align_val);
170170
assert(stats.expect_align(over_align_val));
171171
}
172172
stats.reset();
173173
{
174-
std::__libcpp_deallocate(p, with_size_val, under_align_val);
174+
std::__libcpp_deallocate<char>(p, with_size_val, under_align_val);
175175
assert(stats.expect_size(with_size_val));
176176
}
177177
stats.reset();
@@ -200,13 +200,13 @@ void test_allocator_and_new_match() {
200200
stats.reset();
201201
#elif defined(NO_SIZE)
202202
stats.reset();
203-
#if TEST_STD_VER >= 11
203+
# if TEST_STD_VER >= 11
204204
{
205205
int* x = DoNotOptimize(new int(42));
206206
delete x;
207207
assert(stats.expect_plain());
208208
}
209-
#endif
209+
# endif
210210
stats.reset();
211211
{
212212
AlignedType* a = DoNotOptimize(new AlignedType());
@@ -239,8 +239,7 @@ void test_allocator_and_new_match() {
239239
{
240240
AlignedType* a = DoNotOptimize(new AlignedType());
241241
delete a;
242-
assert(stats.expect_size_align(sizeof(AlignedType),
243-
TEST_ALIGNOF(AlignedType)));
242+
assert(stats.expect_size_align(sizeof(AlignedType), TEST_ALIGNOF(AlignedType)));
244243
}
245244
stats.reset();
246245
#endif

0 commit comments

Comments
 (0)