Skip to content

Commit 5083980

Browse files
authored
[libc] Add hardening for FixedVector data structure and fix exposed bug. (#122159)
Add LIBC_ASSERT statements to FixedVector implementation, and zero out the memory when the elements are removed to flag out-of-bound access and dangling pointer/reference access. This change unmasks the bug in one of FixedVector uses for atexit handlers: dangling reference use, which was actually led to crashes in the wild (with prod blockstore implementation). Fix it in this CL.
1 parent f35b9ad commit 5083980

File tree

4 files changed

+44
-16
lines changed

4 files changed

+44
-16
lines changed

libc/src/__support/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ add_header_library(
267267
HDRS
268268
fixedvector.h
269269
DEPENDS
270+
.libc_assert
270271
libc.src.__support.CPP.array
272+
libc.src.string.memory_utils.inline_memset
271273
)
272274

273275
add_header_library(

libc/src/__support/fixedvector.h

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
#define LLVM_LIBC_SRC___SUPPORT_FIXEDVECTOR_H
1111

1212
#include "src/__support/CPP/array.h"
13-
1413
#include "src/__support/CPP/iterator.h"
14+
#include "src/__support/libc_assert.h"
1515
#include "src/__support/macros/config.h"
16+
#include "src/string/memory_utils/inline_memset.h"
1617

1718
namespace LIBC_NAMESPACE_DECL {
1819

@@ -23,55 +24,76 @@ template <typename T, size_t CAPACITY> class FixedVector {
2324
size_t item_count = 0;
2425

2526
public:
26-
constexpr FixedVector() = default;
27+
LIBC_INLINE constexpr FixedVector() = default;
2728

2829
using iterator = typename cpp::array<T, CAPACITY>::iterator;
29-
constexpr FixedVector(iterator begin, iterator end) : store{}, item_count{} {
30+
LIBC_INLINE constexpr FixedVector(iterator begin, iterator end)
31+
: store{}, item_count{} {
32+
LIBC_ASSERT(begin + CAPACITY >= end);
3033
for (; begin != end; ++begin)
3134
push_back(*begin);
3235
}
3336

3437
using const_iterator = typename cpp::array<T, CAPACITY>::const_iterator;
35-
constexpr FixedVector(const_iterator begin, const_iterator end)
38+
LIBC_INLINE constexpr FixedVector(const_iterator begin, const_iterator end)
3639
: store{}, item_count{} {
40+
LIBC_ASSERT(begin + CAPACITY >= end);
3741
for (; begin != end; ++begin)
3842
push_back(*begin);
3943
}
4044

41-
constexpr FixedVector(size_t count, const T &value) : store{}, item_count{} {
45+
LIBC_INLINE constexpr FixedVector(size_t count, const T &value)
46+
: store{}, item_count{} {
47+
LIBC_ASSERT(count <= CAPACITY);
4248
for (size_t i = 0; i < count; ++i)
4349
push_back(value);
4450
}
4551

46-
constexpr bool push_back(const T &obj) {
52+
LIBC_INLINE constexpr bool push_back(const T &obj) {
4753
if (item_count == CAPACITY)
4854
return false;
4955
store[item_count] = obj;
5056
++item_count;
5157
return true;
5258
}
5359

54-
constexpr const T &back() const { return store[item_count - 1]; }
60+
LIBC_INLINE constexpr const T &back() const {
61+
LIBC_ASSERT(!empty());
62+
return store[item_count - 1];
63+
}
5564

56-
constexpr T &back() { return store[item_count - 1]; }
65+
LIBC_INLINE constexpr T &back() {
66+
LIBC_ASSERT(!empty());
67+
return store[item_count - 1];
68+
}
5769

58-
constexpr bool pop_back() {
70+
LIBC_INLINE constexpr bool pop_back() {
5971
if (item_count == 0)
6072
return false;
73+
inline_memset(&store[item_count - 1], 0, sizeof(T));
6174
--item_count;
6275
return true;
6376
}
6477

65-
constexpr T &operator[](size_t idx) { return store[idx]; }
78+
LIBC_INLINE constexpr T &operator[](size_t idx) {
79+
LIBC_ASSERT(idx < item_count);
80+
return store[idx];
81+
}
6682

67-
constexpr const T &operator[](size_t idx) const { return store[idx]; }
83+
LIBC_INLINE constexpr const T &operator[](size_t idx) const {
84+
LIBC_ASSERT(idx < item_count);
85+
return store[idx];
86+
}
6887

69-
constexpr bool empty() const { return item_count == 0; }
88+
LIBC_INLINE constexpr bool empty() const { return item_count == 0; }
7089

71-
constexpr size_t size() const { return item_count; }
90+
LIBC_INLINE constexpr size_t size() const { return item_count; }
7291

7392
// Empties the store for all practical purposes.
74-
constexpr void reset() { item_count = 0; }
93+
LIBC_INLINE constexpr void reset() {
94+
inline_memset(store.data(), 0, sizeof(T) * item_count);
95+
item_count = 0;
96+
}
7597

7698
// This static method does not free up the resources held by |store|,
7799
// say by calling `free` or something similar. It just does the equivalent
@@ -81,7 +103,9 @@ template <typename T, size_t CAPACITY> class FixedVector {
81103
// dynamically allocated storate. So, the `destroy` method like this
82104
// matches the `destroy` API of those other data structures so that users
83105
// can easily swap one data structure for the other.
84-
static void destroy(FixedVector<T, CAPACITY> *store) { store->reset(); }
106+
LIBC_INLINE static void destroy(FixedVector<T, CAPACITY> *store) {
107+
store->reset();
108+
}
85109

86110
using reverse_iterator = typename cpp::array<T, CAPACITY>::reverse_iterator;
87111
LIBC_INLINE constexpr reverse_iterator rbegin() {

libc/src/stdlib/exit_handler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ LIBC_INLINE void stdc_at_exit_func(void *payload) {
4848
LIBC_INLINE void call_exit_callbacks(ExitCallbackList &callbacks) {
4949
handler_list_mtx.lock();
5050
while (!callbacks.empty()) {
51-
AtExitUnit &unit = callbacks.back();
51+
AtExitUnit unit = callbacks.back();
5252
callbacks.pop_back();
5353
handler_list_mtx.unlock();
5454
unit.callback(unit.payload);

utils/bazel/llvm-project-overlay/libc/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,8 @@ libc_support_library(
670670
deps = [
671671
":__support_cpp_array",
672672
":__support_cpp_iterator",
673+
":__support_libc_assert",
674+
":string_memory_utils",
673675
],
674676
)
675677

0 commit comments

Comments
 (0)