Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/openvic-simulation/core/Assert.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#pragma once

#include <bit> // IWYU pragma: keep for use of cross-library reference for standard library macro definitions

/// Adds macros for memory hardening asserts:
// OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG)
// OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME) - requires FUNC_NAME to be string literal
// OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME) - requires `this->size()` is a valid call and FUNC_NAME to be string literal
// OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME) - requires `!this->empty()` is a valid call and FUNC_NAME to be string literal
// OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME) - requires FUNC_NAME to be string literal
//
// Adds macros for aborting/throwing for strict container errors:
// OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE)
// OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) - requires CLASS_NAME and FUNC_NAME to be string literals

#ifdef __GLIBCXX__
#include <debug/assertions.h>

#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG) __glibcxx_assert(BEGIN <= END)
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME) __glibcxx_requires_non_empty_range(BEGIN, END)

#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME) __glibcxx_requires_subscript(INDEX)
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME) __glibcxx_requires_nonempty()
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME) \
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(IT, end(), FUNC_NAME " called with a non-dereferenceable iterator")

#elif defined(_LIBCPP_VERSION)
#include <__assert>

#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG) _LIBCPP_ASSERT_VALID_INPUT_RANGE(BEGIN <= END, MSG)
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME) \
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, FUNC_NAME " called with an invalid range")

#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME) \
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(INDEX < size(), FUNC_NAME " index out of bounds")
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME) \
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), FUNC_NAME " called on an empty container")
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME) \
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(IT != end(), FUNC_NAME " called with a non-dereferenceable iterator")

#elif defined(_MSVC_STL_VERSION) && _MSVC_STL_HARDENING == 1
#include <yvals.h>

#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG) _STL_VERIFY(BEGIN <= END, MSG)
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME) \
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, FUNC_NAME " called with an invalid range")

#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME) _STL_VERIFY(INDEX < size(), FUNC_NAME " index out of bounds")
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME) _STL_VERIFY(!empty(), FUNC_NAME " called on an empty container")
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME) \
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(IT, end(), FUNC_NAME " called with a non-dereferenceable iterator")

#else
#define OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(BEGIN, END, MSG)
#define OV_HARDEN_ASSERT_NONEMPTY_RANGE(BEGIN, END, FUNC_NAME)
#define OV_HARDEN_ASSERT_ACCESS(INDEX, FUNC_NAME)
#define OV_HARDEN_ASSERT_NONEMPTY(FUNC_NAME)
#define OV_HARDEN_ASSERT_VALID_ITERATOR(IT, FUNC_NAME)

// clang-format off
# if defined(_GLIBCXX_ASSERTIONS) || _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_FAST || _MSVC_STL_HARDENING == 1
#warning "Unsupported standard library for memory hardening, hardening asserts will be ignored."
# endif
// clang-format on

#endif

#ifdef __GLIBCXX__
#if __has_include(<bits/functexcept.h>)
#include <bits/functexcept.h>
#elif __has_include(<bits/stdexcept_throw.h>)
#include <bits/stdexcept_throw.h>
#else
#include <cstdlib>
#warning "Unknown GLIBCXX library version"
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) std::abort()
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::abort()
#endif

#ifndef OV_THROW_OUT_OF_RANGE
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) \
std::__throw_out_of_range_fmt( \
__N("%s::%s: %s (which is %zu) >= this->size() (which is %zu)"), CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE \
)
#endif

#ifndef OV_THROW_LENGTH_ERROR
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::__throw_length_error(__N(CLASS_NAME "::" FUNC_NAME))
#endif

#elif defined(_LIBCPP_VERSION) && __has_include(<stdexcept>)
#include <stdexcept>

#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) std::__throw_out_of_range(CLASS_NAME)
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::__throw_length_error(CLASS_NAME)

#elif defined(_MSVC_STL_VERSION) && __has_include(<xutility>)
#include <xutility>

#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) \
std::_Xout_of_range("invalid " CLASS_NAME " subscript")
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::_Xlength_error(CLASS_NAME " too long")
#endif

#ifndef OV_THROW_OUT_OF_RANGE
#include <cstdlib>
#define OV_THROW_OUT_OF_RANGE(CLASS_NAME, FUNC_NAME, VAR_NAME, VAR, SIZE) std::abort()
#endif

#ifndef OV_THROW_LENGTH_ERROR
#include <cstdlib>
#define OV_THROW_LENGTH_ERROR(CLASS_NAME, FUNC_NAME) std::abort()
#endif
4 changes: 2 additions & 2 deletions src/openvic-simulation/types/Colour.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,10 +871,10 @@ struct fmt::formatter<T> {
size_t i = 0;
for (char& c : lower) {
c = std::tolower(result[i]);
if (c == '\0') {
++i;
if (i == result.size()) {
break;
}
++i;
}
return detail::write(out, string_view { lower.data(), i }, specs, ctx.locale());
}
Expand Down
39 changes: 29 additions & 10 deletions src/openvic-simulation/types/CowVector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <type_traits>
#include <vector>

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

[[noreturn]] void _abort_on_out_of_range(const char* func_name, const char* var_name, size_t var, size_t size) const {
OV_THROW_OUT_OF_RANGE("cow_vector", func_name, var_name, var, size);
}

public:
using value_type = T;
using allocator_type = Allocator;
Expand Down Expand Up @@ -166,19 +171,24 @@ namespace OpenVic {
}

const_reference at(size_type pos) const {
// TODO: crash on boundary violation
return (*this)[pos];
if (OV_unlikely(pos >= size())) {
_abort_on_out_of_range("at", "pos", pos, size());
}
return _data->array[pos];
}

const_reference operator[](size_type pos) const {
OV_HARDEN_ASSERT_ACCESS(pos, "operator[]");
return _data->array[pos];
}

const_reference front() const {
OV_HARDEN_ASSERT_NONEMPTY("front");
return *_data->array;
}

const_reference back() const {
OV_HARDEN_ASSERT_NONEMPTY("back");
return *_data->array_end;
}

Expand Down Expand Up @@ -329,9 +339,8 @@ namespace OpenVic {

template<typename InputIt>
inline difference_type _validate_iterator_difference(InputIt first, InputIt last) {
difference_type result = last - first;
// TODO: crash on negative result
return result;
OV_HARDEN_ASSERT_VALID_RANGE_MESSAGE(first, last, "_validate_iterator_difference called with invalid range");
return last - first;
}

struct for_overwrite_t {};
Expand Down Expand Up @@ -367,19 +376,24 @@ namespace OpenVic {
}

reference at(size_type pos) {
return (*this)[pos];
if (OV_unlikely(pos >= size())) {
_abort_on_out_of_range("at", "pos", pos, size());
}
return _data->array[pos];
}

reference operator[](size_type pos) {
// TODO: crash on boundary violation
OV_HARDEN_ASSERT_ACCESS(pos, "operator[]");
return _data->array[pos];
}

reference front() {
OV_HARDEN_ASSERT_NONEMPTY("front");
return *_data->array;
}

reference back() {
OV_HARDEN_ASSERT_NONEMPTY("back");
return *_data->array_end;
}

Expand All @@ -404,8 +418,8 @@ namespace OpenVic {
}

void reserve(size_type count) {
if (count > max_size()) {
// TODO: crash
if (OV_unlikely(count > max_size())) {
OV_THROW_LENGTH_ERROR("cow_vector::writer", "reserve");
}
if (capacity() >= count) {
return;
Expand Down Expand Up @@ -510,6 +524,7 @@ namespace OpenVic {
}

iterator erase(const_iterator pos) {
OV_HARDEN_ASSERT_VALID_ITERATOR(pos, "erase(const_iterator)");
if (pos + 1 != end()) {
std::move(pos + 1, end(), pos);
}
Expand Down Expand Up @@ -561,7 +576,7 @@ namespace OpenVic {
}

void pop_back() {
// TODO: assert if empty
OV_HARDEN_ASSERT_NONEMPTY("pop_back");
--_data->array_end;
allocator_traits::destroy(alloc, _data->array_end);
}
Expand Down Expand Up @@ -1081,6 +1096,10 @@ namespace OpenVic {
_range_insert(end(), first, last, std::iterator_traits<decltype(first)>::iterator_category());
}
}

[[noreturn]] void _abort_on_out_of_range(const char* func_name, const char* var_name, size_t var, size_t size) const {
OV_THROW_OUT_OF_RANGE("cow_vector::writer", func_name, var_name, var, size);
}
};

template<typename T, typename Allocator>
Expand Down
13 changes: 7 additions & 6 deletions src/openvic-simulation/types/FixedVector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <memory>
#include <utility>

#include "openvic-simulation/core/Assert.hpp"
#include "openvic-simulation/core/template/Concepts.hpp"
#include "openvic-simulation/core/Typedefs.hpp"

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

T& operator[](const size_t index) {
assert(index < _size);
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
return _data_start_ptr[index];
}
const T& operator[](const size_t index) const {
assert(index < _size);
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
return _data_start_ptr[index];
}

T& front() {
assert(!empty());
OV_HARDEN_ASSERT_NONEMPTY("front");
return *begin();
}
const T& front() const {
assert(!empty());
OV_HARDEN_ASSERT_NONEMPTY("front");
return *cbegin();
}
T& back() {
assert(!empty());
OV_HARDEN_ASSERT_NONEMPTY("back");
return *(end()-1);
}
const T& back() const {
assert(!empty());
OV_HARDEN_ASSERT_NONEMPTY("back");
return *(cend()-1);
}

Expand Down
39 changes: 30 additions & 9 deletions src/openvic-simulation/types/RingBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <range/v3/range/concepts.hpp>
#include <range/v3/view/subrange.hpp>

#include "openvic-simulation/core/Assert.hpp"
#include "openvic-simulation/core/Typedefs.hpp"

namespace OpenVic {
Expand Down Expand Up @@ -215,34 +216,49 @@ namespace OpenVic {
allocator_type get_allocator() const {
return _allocator;
}

private:
reference _unsafe_access(const size_type index) {
return _data[_ring_wrap(_offset + index, capacity())];
}
const_reference _unsafe_access(const size_type index) const {
return _data[_ring_wrap(_offset + index, capacity())];
}

public:
reference front() {
return at(0);
OV_HARDEN_ASSERT_NONEMPTY("front");
return _unsafe_access(0);
}
reference back() {
return at(size() - 1);
OV_HARDEN_ASSERT_NONEMPTY("back");
return _unsafe_access(size() - 1);
}
const_reference back() const {
return at(size() - 1);
OV_HARDEN_ASSERT_NONEMPTY("back");
return _unsafe_access(size() - 1);
}

const_reference operator[](const size_type index) const {
return _data[_ring_wrap(_offset + index, capacity())];
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
return _unsafe_access(index);
}
reference operator[](const size_type index) {
return _data[_ring_wrap(_offset + index, capacity())];
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
return _unsafe_access(index);
}

const_reference at(const size_type index) const {
if (OV_unlikely(index >= size())) {
std::abort();
_abort_on_out_of_range("at", "index", index, size());
}
return (*this)[index];
return _unsafe_access(index);
}
reference at(const size_type index) {
if (OV_unlikely(index >= size())) {
std::abort();
_abort_on_out_of_range("at", "index", index, size());
}
return (*this)[index];
return _unsafe_access(index);
}

iterator begin() noexcept {
Expand Down Expand Up @@ -693,6 +709,7 @@ namespace OpenVic {
return erase(pos, last);
}
iterator erase(const_iterator pos) noexcept(noexcept(erase(pos, 1))) {
OV_HARDEN_ASSERT_VALID_ITERATOR(pos, "erase(const_iterator)");
return erase(pos, 1);
}

Expand Down Expand Up @@ -764,6 +781,10 @@ namespace OpenVic {
return (ring_index <= ring_capacity) ? ring_index : ring_index - ring_capacity - 1;
}

[[noreturn]] void _abort_on_out_of_range(const char* func_name, const char* var_name, size_t var, size_t size) const {
OV_THROW_OUT_OF_RANGE("RingBuffer", func_name, var_name, var, size);
}

// The start of the dynamically allocated backing array.
pointer _data = nullptr;
// The next position to write to for push_back().
Expand Down
2 changes: 2 additions & 0 deletions src/openvic-simulation/types/StackString.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstdint>
#include <string_view>

#include "openvic-simulation/core/Assert.hpp"
#include "openvic-simulation/utility/Containers.hpp"

namespace OpenVic {
Expand Down Expand Up @@ -40,6 +41,7 @@ namespace OpenVic {
}

constexpr decltype(_array)::const_reference operator[](size_t index) const {
OV_HARDEN_ASSERT_ACCESS(index, "operator[]");
return _array[index];
}

Expand Down