Skip to content

Commit e924f71

Browse files
author
Alexey Samsonov
committed
[libc] Add hardening for FixedVector data structure and fix exposed bug.
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 e93181b commit e924f71

File tree

4 files changed

+40
-19
lines changed

4 files changed

+40
-19
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: 35 additions & 18 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,71 @@ 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) : store{}, item_count{} {
3031
for (; begin != end; ++begin)
31-
push_back(*begin);
32+
LIBC_ASSERT(push_back(*begin));
3233
}
3334

3435
using const_iterator = typename cpp::array<T, CAPACITY>::const_iterator;
35-
constexpr FixedVector(const_iterator begin, const_iterator end)
36+
LIBC_INLINE constexpr FixedVector(const_iterator begin, const_iterator end)
3637
: store{}, item_count{} {
3738
for (; begin != end; ++begin)
38-
push_back(*begin);
39+
LIBC_ASSERT(push_back(*begin));
3940
}
4041

41-
constexpr FixedVector(size_t count, const T &value) : store{}, item_count{} {
42+
LIBC_INLINE constexpr FixedVector(size_t count, const T &value) : store{}, item_count{} {
4243
for (size_t i = 0; i < count; ++i)
43-
push_back(value);
44+
LIBC_ASSERT(push_back(value));
4445
}
4546

46-
constexpr bool push_back(const T &obj) {
47+
LIBC_INLINE constexpr bool push_back(const T &obj) {
4748
if (item_count == CAPACITY)
4849
return false;
4950
store[item_count] = obj;
5051
++item_count;
5152
return true;
5253
}
5354

54-
constexpr const T &back() const { return store[item_count - 1]; }
55+
LIBC_INLINE constexpr const T &back() const {
56+
LIBC_ASSERT(!empty());
57+
return store[item_count - 1];
58+
}
5559

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

58-
constexpr bool pop_back() {
65+
LIBC_INLINE constexpr bool pop_back() {
5966
if (item_count == 0)
6067
return false;
68+
inline_memset(&store[item_count - 1], 0, sizeof(T));
6169
--item_count;
6270
return true;
6371
}
6472

65-
constexpr T &operator[](size_t idx) { return store[idx]; }
73+
LIBC_INLINE constexpr T &operator[](size_t idx) {
74+
LIBC_ASSERT(idx < item_count);
75+
return store[idx];
76+
}
6677

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

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

71-
constexpr size_t size() const { return item_count; }
85+
LIBC_INLINE constexpr size_t size() const { return item_count; }
7286

7387
// Empties the store for all practical purposes.
74-
constexpr void reset() { item_count = 0; }
88+
LIBC_INLINE constexpr void reset() {
89+
inline_memset(store.data(), 0, sizeof(T) * item_count);
90+
item_count = 0;
91+
}
7592

7693
// This static method does not free up the resources held by |store|,
7794
// say by calling `free` or something similar. It just does the equivalent
@@ -81,7 +98,7 @@ template <typename T, size_t CAPACITY> class FixedVector {
8198
// dynamically allocated storate. So, the `destroy` method like this
8299
// matches the `destroy` API of those other data structures so that users
83100
// can easily swap one data structure for the other.
84-
static void destroy(FixedVector<T, CAPACITY> *store) { store->reset(); }
101+
LIBC_INLINE static void destroy(FixedVector<T, CAPACITY> *store) { store->reset(); }
85102

86103
using reverse_iterator = typename cpp::array<T, CAPACITY>::reverse_iterator;
87104
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)