Skip to content

Commit 32c1523

Browse files
committed
Merge bitcoin/bitcoin#27930: util: Don't derive secure_allocator from std::allocator
07c59ed Don't derive secure_allocator from std::allocator (Casey Carter) Pull request description: Giving the C++ Standard Committee control of the public interface of your type means they will break it. C++23 adds a new `allocate_at_least` member to `std::allocator`. Very bad things happen when, say, `std::vector` uses `allocate_at_least` from `secure_allocator`'s base to allocate memory which it then tries to free with `secure_allocator::deallocate`. (Discovered by microsoft/STL#3712, which will be reverted by microsoft/STL#3819 before it ships.) ACKs for top commit: jonatack: re-ACK 07c59ed no change since my previous ACK apart from squashing the commits achow101: ACK 07c59ed john-moffett: ACK 07c59ed Reviewed and tested. Performance appears unaffected in my environment. Tree-SHA512: 23606c40414d325f5605a9244d4dd50907fdf5f2fbf70f336accb3a2cb98baa8acd2972f46eab1b7fdec1d28a843a96b06083cd2d09791cda7c90ee218e5bbd5
2 parents 1ed8a0f + 07c59ed commit 32c1523

File tree

2 files changed

+39
-36
lines changed

2 files changed

+39
-36
lines changed

src/support/allocators/secure.h

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,14 @@
1717
// out of memory and clears its contents before deletion.
1818
//
1919
template <typename T>
20-
struct secure_allocator : public std::allocator<T> {
21-
using base = std::allocator<T>;
22-
using traits = std::allocator_traits<base>;
23-
using size_type = typename traits::size_type;
24-
using difference_type = typename traits::difference_type;
25-
using pointer = typename traits::pointer;
26-
using const_pointer = typename traits::const_pointer;
27-
using value_type = typename traits::value_type;
28-
secure_allocator() noexcept {}
29-
secure_allocator(const secure_allocator& a) noexcept : base(a) {}
20+
struct secure_allocator {
21+
using value_type = T;
22+
23+
secure_allocator() = default;
3024
template <typename U>
31-
secure_allocator(const secure_allocator<U>& a) noexcept : base(a)
32-
{
33-
}
34-
~secure_allocator() noexcept {}
35-
template <typename Other>
36-
struct rebind {
37-
typedef secure_allocator<Other> other;
38-
};
25+
secure_allocator(const secure_allocator<U>&) noexcept {}
3926

40-
T* allocate(std::size_t n, const void* hint = nullptr)
27+
T* allocate(std::size_t n)
4128
{
4229
T* allocation = static_cast<T*>(LockedPoolManager::Instance().alloc(sizeof(T) * n));
4330
if (!allocation) {
@@ -53,6 +40,17 @@ struct secure_allocator : public std::allocator<T> {
5340
}
5441
LockedPoolManager::Instance().free(p);
5542
}
43+
44+
template <typename U>
45+
friend bool operator==(const secure_allocator&, const secure_allocator<U>&) noexcept
46+
{
47+
return true;
48+
}
49+
template <typename U>
50+
friend bool operator!=(const secure_allocator&, const secure_allocator<U>&) noexcept
51+
{
52+
return false;
53+
}
5654
};
5755

5856
// This is exactly like std::string, but with a custom allocator.

src/support/allocators/zeroafterfree.h

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,36 @@
1212
#include <vector>
1313

1414
template <typename T>
15-
struct zero_after_free_allocator : public std::allocator<T> {
16-
using base = std::allocator<T>;
17-
using traits = std::allocator_traits<base>;
18-
using size_type = typename traits::size_type;
19-
using difference_type = typename traits::difference_type;
20-
using pointer = typename traits::pointer;
21-
using const_pointer = typename traits::const_pointer;
22-
using value_type = typename traits::value_type;
23-
zero_after_free_allocator() noexcept {}
24-
zero_after_free_allocator(const zero_after_free_allocator& a) noexcept : base(a) {}
15+
struct zero_after_free_allocator {
16+
using value_type = T;
17+
18+
zero_after_free_allocator() noexcept = default;
2519
template <typename U>
26-
zero_after_free_allocator(const zero_after_free_allocator<U>& a) noexcept : base(a)
20+
zero_after_free_allocator(const zero_after_free_allocator<U>&) noexcept
21+
{
22+
}
23+
24+
T* allocate(std::size_t n)
2725
{
26+
return std::allocator<T>{}.allocate(n);
2827
}
29-
~zero_after_free_allocator() noexcept {}
30-
template <typename Other>
31-
struct rebind {
32-
typedef zero_after_free_allocator<Other> other;
33-
};
3428

3529
void deallocate(T* p, std::size_t n)
3630
{
3731
if (p != nullptr)
3832
memory_cleanse(p, sizeof(T) * n);
39-
std::allocator<T>::deallocate(p, n);
33+
std::allocator<T>{}.deallocate(p, n);
34+
}
35+
36+
template <typename U>
37+
friend bool operator==(const zero_after_free_allocator&, const zero_after_free_allocator<U>&) noexcept
38+
{
39+
return true;
40+
}
41+
template <typename U>
42+
friend bool operator!=(const zero_after_free_allocator&, const zero_after_free_allocator<U>&) noexcept
43+
{
44+
return false;
4045
}
4146
};
4247

0 commit comments

Comments
 (0)