Skip to content

Commit 4473211

Browse files
committed
Add memory hardening assert macros
Add hardening to CowVector Add hardening to FixedVector Add hardening to RingBuffer Add hardening to StackString Add abort to CowVector::at Add abort to CowVector::reserve
1 parent 5646230 commit 4473211

File tree

6 files changed

+172
-27
lines changed

6 files changed

+172
-27
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#pragma once
2+
3+
#include <bit> // IWYU pragma: keep for use of cross-library reference for standard library macro definitions
4+
5+
#ifdef __GLIBCXX__
6+
#include <debug/assertions.h>
7+
8+
#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG) __glibcxx_assert(BEGIN <= END)
9+
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME) __glibcxx_requires_non_empty_range(BEGIN, END)
10+
11+
#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME) __glibcxx_requires_subscript(INDEX)
12+
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME) __glibcxx_requires_nonempty()
13+
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME) \
14+
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(IT, end(), FUNC_NAME " called with a non-dereferenceable iterator")
15+
16+
#elif defined(_LIBCPP_VERSION)
17+
#include <__assert>
18+
19+
#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG) _LIBCPP_ASSERT_VALID_INPUT_RANGE(BEGIN <= END, MSG)
20+
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME) \
21+
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, FUNC_NAME " called with an invalid range")
22+
23+
#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME) \
24+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(INDEX < size(), FUNC_NAME " index out of bounds")
25+
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME) \
26+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), FUNC_NAME " called on an empty container")
27+
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME) \
28+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(IT != end(), FUNC_NAME " called with a non-dereferenceable iterator")
29+
30+
#elif defined(_MSVC_STL_VERSION) && _MSVC_STL_HARDENING == 1
31+
#include <yvals.h>
32+
33+
#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG) _STL_VERIFY(BEGIN <= END, MSG)
34+
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME) \
35+
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, FUNC_NAME " called with an invalid range")
36+
37+
#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME) _STL_VERIFY(INDEX < size(), FUNC_NAME " index out of bounds")
38+
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME) _STL_VERIFY(!empty(), FUNC_NAME " called on an empty container")
39+
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME) \
40+
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(IT, end(), FUNC_NAME " called with a non-dereferenceable iterator")
41+
42+
#else
43+
#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG)
44+
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME)
45+
#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME)
46+
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME)
47+
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME)
48+
49+
// clang-format off
50+
# if defined(_GLIBCXX_ASSERTIONS) || _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_FAST || _MSVC_STL_HARDENING == 1
51+
#warning "Unsupported standard library for memory hardening, hardening asserts will be ignored."
52+
# endif
53+
// clang-format on
54+
55+
#endif
56+
57+
#ifdef __GLIBCXX__
58+
#if __has_include(<bits/functexcept.h>)
59+
#include <bits/functexcept.h>
60+
#elif __has_include(<bits/stdexcept_throw.h>)
61+
#include <bits/stdexcept_throw.h>
62+
#else
63+
#include <cstdlib>
64+
#warning "Unknown GLIBCXX library version"
65+
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) std::abort()
66+
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::abort()
67+
#endif
68+
69+
#ifndef OV_THROW_OUT_OF_RANGE
70+
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) \
71+
std::__throw_out_of_range_fmt( \
72+
__N("%s::%s: %s (which is %zu) >= this->size() (which is %zu)"), CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE \
73+
)
74+
#endif
75+
76+
#ifndef OV_THROW_LENGTH_ERROR
77+
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::__throw_length_error(__N(CLASS_NAME "::" FUNC_NAME))
78+
#endif
79+
80+
#elif defined(_LIBCPP_VERSION) && __has_include(<stdexcept>)
81+
#include <stdexcept>
82+
83+
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) std::__throw_out_of_range(CLASS_NAME)
84+
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::__throw_length_error(CLASS_NAME)
85+
86+
#elif defined(_MSVC_STL_VERSION) && __has_include(<xutility>)
87+
#include <xutility>
88+
89+
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) \
90+
std::_Xout_of_range("invalid " CLASS_NAME " subscript")
91+
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::_Xlength_error(CLASS_NAME " too long")
92+
#endif
93+
94+
#ifndef OV_THROW_OUT_OF_RANGE
95+
#include <cstdlib>
96+
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) std::abort()
97+
#endif
98+
99+
#ifndef OV_THROW_LENGTH_ERROR
100+
#include <cstdlib>
101+
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::abort()
102+
#endif

src/openvic-simulation/types/Colour.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -871,10 +871,10 @@ struct fmt::formatter<T> {
871871
size_t i = 0;
872872
for (char& c : lower) {
873873
c = std::tolower(result[i]);
874-
if (c == '\0') {
874+
++i;
875+
if (i == result.size()) {
875876
break;
876877
}
877-
++i;
878878
}
879879
return detail::write(out, string_view { lower.data(), i }, specs, ctx.locale());
880880
}

src/openvic-simulation/types/CowVector.hpp

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <type_traits>
1212
#include <vector>
1313

14+
#include "openvic-simulation/core/Assert.hpp"
1415
#include "openvic-simulation/types/BasicIterator.hpp"
1516
#include "openvic-simulation/utility/Allocator.hpp"
1617
#include "openvic-simulation/core/Compare.hpp"
@@ -39,6 +40,10 @@ namespace OpenVic {
3940
OV_ALWAYS_INLINE cow_vector(allocate_tag_t, Allocator const& alloc, size_t reserve)
4041
: alloc(alloc), _data(_allocate_payload(reserve)) {}
4142

43+
[[noreturn]] void _abort_on_out_of_range(const char* func_name, const char* var_name, size_t var, size_t size) const {
44+
OV_THROW_OUT_OF_RANGE("cow_vector", func_name, var_name, var, size);
45+
}
46+
4247
public:
4348
using value_type = T;
4449
using allocator_type = Allocator;
@@ -166,19 +171,24 @@ namespace OpenVic {
166171
}
167172

168173
const_reference at(size_type pos) const {
169-
// TODO: crash on boundary violation
170-
return (*this)[pos];
174+
if (OV_unlikely(pos >= size())) {
175+
_abort_on_out_of_range("at", "pos", pos, size());
176+
}
177+
return _data->array[pos];
171178
}
172179

173180
const_reference operator[](size_type pos) const {
181+
OV_HARDEN_ASSERT_ACCESS(pos, "operator[]");
174182
return _data->array[pos];
175183
}
176184

177185
const_reference front() const {
186+
OV_HARDEN_ASSERT_NONEMPTY("front");
178187
return *_data->array;
179188
}
180189

181190
const_reference back() const {
191+
OV_HARDEN_ASSERT_NONEMPTY("back");
182192
return *_data->array_end;
183193
}
184194

@@ -329,9 +339,8 @@ namespace OpenVic {
329339

330340
template<typename InputIt>
331341
inline difference_type _validate_iterator_difference(InputIt first, InputIt last) {
332-
difference_type result = last - first;
333-
// TODO: crash on negative result
334-
return result;
342+
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(first, last, "_validate_iterator_difference called with invalid range");
343+
return last - first;
335344
}
336345

337346
struct for_overwrite_t {};
@@ -367,19 +376,24 @@ namespace OpenVic {
367376
}
368377

369378
reference at(size_type pos) {
370-
return (*this)[pos];
379+
if (OV_unlikely(pos >= size())) {
380+
_abort_on_out_of_range("at", "pos", pos, size());
381+
}
382+
return _data->array[pos];
371383
}
372384

373385
reference operator[](size_type pos) {
374-
// TODO: crash on boundary violation
386+
OV_HARDEN_ASSERT_ACCESS(pos, "operator[]");
375387
return _data->array[pos];
376388
}
377389

378390
reference front() {
391+
OV_HARDEN_ASSERT_NONEMPTY("front");
379392
return *_data->array;
380393
}
381394

382395
reference back() {
396+
OV_HARDEN_ASSERT_NONEMPTY("back");
383397
return *_data->array_end;
384398
}
385399

@@ -404,8 +418,8 @@ namespace OpenVic {
404418
}
405419

406420
void reserve(size_type count) {
407-
if (count > max_size()) {
408-
// TODO: crash
421+
if (OV_unlikely(count > max_size())) {
422+
OV_THROW_LENGTH_ERROR("cow_vector::writer", "reserve");
409423
}
410424
if (capacity() >= count) {
411425
return;
@@ -510,6 +524,7 @@ namespace OpenVic {
510524
}
511525

512526
iterator erase(const_iterator pos) {
527+
OV_HARDEN_ASSERT_VALID_ITERATOR(pos, "erase(const_iterator)");
513528
if (pos + 1 != end()) {
514529
std::move(pos + 1, end(), pos);
515530
}
@@ -561,7 +576,7 @@ namespace OpenVic {
561576
}
562577

563578
void pop_back() {
564-
// TODO: assert if empty
579+
OV_HARDEN_ASSERT_NONEMPTY("pop_back");
565580
--_data->array_end;
566581
allocator_traits::destroy(alloc, _data->array_end);
567582
}
@@ -1081,6 +1096,10 @@ namespace OpenVic {
10811096
_range_insert(end(), first, last, std::iterator_traits<decltype(first)>::iterator_category());
10821097
}
10831098
}
1099+
1100+
[[noreturn]] void _abort_on_out_of_range(const char* func_name, const char* var_name, size_t var, size_t size) const {
1101+
OV_THROW_OUT_OF_RANGE("cow_vector::writer", func_name, var_name, var, size);
1102+
}
10841103
};
10851104

10861105
template<typename T, typename Allocator>

src/openvic-simulation/types/FixedVector.hpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <memory>
77
#include <utility>
88

9+
#include "openvic-simulation/core/Assert.hpp"
910
#include "openvic-simulation/core/template/Concepts.hpp"
1011
#include "openvic-simulation/core/Typedefs.hpp"
1112

@@ -138,28 +139,28 @@ namespace OpenVic::_detail {
138139
const_reverse_iterator crend() const { return const_reverse_iterator(begin()); }
139140

140141
T& operator[](const size_t index) {
141-
assert(index < _size);
142+
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
142143
return _data_start_ptr[index];
143144
}
144145
const T& operator[](const size_t index) const {
145-
assert(index < _size);
146+
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
146147
return _data_start_ptr[index];
147148
}
148149

149150
T& front() {
150-
assert(!empty());
151+
OV_HARDEN_ASSERT_NONEMPTY("front");
151152
return *begin();
152153
}
153154
const T& front() const {
154-
assert(!empty());
155+
OV_HARDEN_ASSERT_NONEMPTY("front");
155156
return *cbegin();
156157
}
157158
T& back() {
158-
assert(!empty());
159+
OV_HARDEN_ASSERT_NONEMPTY("back");
159160
return *(end()-1);
160161
}
161162
const T& back() const {
162-
assert(!empty());
163+
OV_HARDEN_ASSERT_NONEMPTY("back");
163164
return *(cend()-1);
164165
}
165166

src/openvic-simulation/types/RingBuffer.hpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <range/v3/range/concepts.hpp>
1919
#include <range/v3/view/subrange.hpp>
2020

21+
#include "openvic-simulation/core/Assert.hpp"
2122
#include "openvic-simulation/core/Typedefs.hpp"
2223

2324
namespace OpenVic {
@@ -215,34 +216,49 @@ namespace OpenVic {
215216
allocator_type get_allocator() const {
216217
return _allocator;
217218
}
219+
220+
private:
221+
reference _unsafe_access(const size_type index) {
222+
return _data[_ring_wrap(_offset + index, capacity())];
223+
}
224+
const_reference _unsafe_access(const size_type index) const {
225+
return _data[_ring_wrap(_offset + index, capacity())];
226+
}
227+
228+
public:
218229
reference front() {
219-
return at(0);
230+
OV_HARDEN_ASSERT_NONEMPTY("front");
231+
return _unsafe_access(0);
220232
}
221233
reference back() {
222-
return at(size() - 1);
234+
OV_HARDEN_ASSERT_NONEMPTY("back");
235+
return _unsafe_access(size() - 1);
223236
}
224237
const_reference back() const {
225-
return at(size() - 1);
238+
OV_HARDEN_ASSERT_NONEMPTY("back");
239+
return _unsafe_access(size() - 1);
226240
}
227241

228242
const_reference operator[](const size_type index) const {
229-
return _data[_ring_wrap(_offset + index, capacity())];
243+
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
244+
return _unsafe_access(index);
230245
}
231246
reference operator[](const size_type index) {
232-
return _data[_ring_wrap(_offset + index, capacity())];
247+
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
248+
return _unsafe_access(index);
233249
}
234250

235251
const_reference at(const size_type index) const {
236252
if (OV_unlikely(index >= size())) {
237-
std::abort();
253+
_abort_on_out_of_range("at", "index", index, size());
238254
}
239-
return (*this)[index];
255+
return _unsafe_access(index);
240256
}
241257
reference at(const size_type index) {
242258
if (OV_unlikely(index >= size())) {
243-
std::abort();
259+
_abort_on_out_of_range("at", "index", index, size());
244260
}
245-
return (*this)[index];
261+
return _unsafe_access(index);
246262
}
247263

248264
iterator begin() noexcept {
@@ -693,6 +709,7 @@ namespace OpenVic {
693709
return erase(pos, last);
694710
}
695711
iterator erase(const_iterator pos) noexcept(noexcept(erase(pos, 1))) {
712+
OV_HARDEN_ASSERT_VALID_ITERATOR(pos, "erase(const_iterator)");
696713
return erase(pos, 1);
697714
}
698715

@@ -764,6 +781,10 @@ namespace OpenVic {
764781
return (ring_index <= ring_capacity) ? ring_index : ring_index - ring_capacity - 1;
765782
}
766783

784+
[[noreturn]] void _abort_on_out_of_range(const char* func_name, const char* var_name, size_t var, size_t size) const {
785+
OV_THROW_OUT_OF_RANGE("RingBuffer", func_name, var_name, var, size);
786+
}
787+
767788
// The start of the dynamically allocated backing array.
768789
pointer _data = nullptr;
769790
// The next position to write to for push_back().

src/openvic-simulation/types/StackString.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <cstdint>
66
#include <string_view>
77

8+
#include "openvic-simulation/core/Assert.hpp"
89
#include "openvic-simulation/utility/Containers.hpp"
910

1011
namespace OpenVic {
@@ -40,6 +41,7 @@ namespace OpenVic {
4041
}
4142

4243
constexpr decltype(_array)::const_reference operator[](size_t index) const {
44+
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
4345
return _array[index];
4446
}
4547

0 commit comments

Comments
 (0)