From c364e3024077082e9555cd1fc4b1a663efaae07e Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 30 Jan 2021 20:07:30 -0800 Subject: [PATCH] [kutil] Flag static allocated vectors ktuil::vector can take a static area of memory as its initial memory, but the case was never handled where it outgrew that memory and had to reallocate. Steal the high bit from the capacity value to indicate the current memory should not be kfree()'d. Also added checks in the heap allocator to make sure pointers look valid. --- src/kernel/vm_space.cpp | 5 ++-- src/libraries/kutil/heap_allocator.cpp | 24 ++++++++++++------- .../kutil/include/kutil/heap_allocator.h | 8 +++---- src/libraries/kutil/include/kutil/vector.h | 23 +++++++++++------- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/kernel/vm_space.cpp b/src/kernel/vm_space.cpp index f1052d0..1e8e2a2 100644 --- a/src/kernel/vm_space.cpp +++ b/src/kernel/vm_space.cpp @@ -7,7 +7,8 @@ #include "vm_space.h" // The initial memory for the array of areas for the kernel space -static uint64_t kernel_areas[16]; +constexpr size_t num_kernel_areas = 8; +static uint64_t kernel_areas[num_kernel_areas * 2]; int vm_space::area::compare(const vm_space::area &o) const @@ -28,7 +29,7 @@ vm_space::area::operator==(const vm_space::area &o) const vm_space::vm_space(page_table *p) : m_kernel {true}, m_pml4 {p}, - m_areas {reinterpret_cast(kernel_areas), 0, 8} + m_areas {reinterpret_cast(kernel_areas), 0, num_kernel_areas} {} vm_space::vm_space() : diff --git a/src/libraries/kutil/heap_allocator.cpp b/src/libraries/kutil/heap_allocator.cpp index cdafd24..7dd66d2 100644 --- a/src/libraries/kutil/heap_allocator.cpp +++ b/src/libraries/kutil/heap_allocator.cpp @@ -62,10 +62,13 @@ private: }; -heap_allocator::heap_allocator() : m_next(0), m_size(0) {} +heap_allocator::heap_allocator() : m_start {0}, m_end {0} {} heap_allocator::heap_allocator(uintptr_t start, size_t size) : - m_next(start), m_size(size), m_allocated_size(0) + m_start {start}, + m_end {start+size}, + m_blocks {0}, + m_allocated_size {0} { kutil::memset(m_free, 0, sizeof(m_free)); } @@ -97,6 +100,11 @@ heap_allocator::free(void *p) { if (!p) return; + static constexpr uintptr_t bad_mask = (1 << min_order) - 1; + uintptr_t addr = reinterpret_cast(p); + kassert(addr >= m_start && addr < m_end && !(addr & bad_mask), + "Attempt to free non-heap pointer"); + mem_header *header = reinterpret_cast(p); header -= 1; // p points after the header header->set_used(false); @@ -133,12 +141,12 @@ heap_allocator::ensure_block(unsigned order) if (order == max_order) { size_t bytes = (1 << max_order); - if (bytes <= m_size) { - mem_header *next = reinterpret_cast(m_next); - new (next) mem_header(nullptr, nullptr, order); - get_free(order) = next; - m_next += bytes; - m_size -= bytes; + uintptr_t next = m_start + m_blocks * bytes; + if (next + bytes <= m_end) { + mem_header *nextp = reinterpret_cast(next); + new (nextp) mem_header(nullptr, nullptr, order); + get_free(order) = nextp; + ++m_blocks; } } else { mem_header *orig = pop_free(order + 1); diff --git a/src/libraries/kutil/include/kutil/heap_allocator.h b/src/libraries/kutil/include/kutil/heap_allocator.h index 3c125f8..43b2dab 100644 --- a/src/libraries/kutil/include/kutil/heap_allocator.h +++ b/src/libraries/kutil/include/kutil/heap_allocator.h @@ -23,11 +23,11 @@ public: /// \arg length The amount of memory to allocate, in bytes /// \returns A pointer to the allocated memory, or nullptr if /// allocation failed. - virtual void * allocate(size_t length); + void * allocate(size_t length); /// Free a previous allocation. /// \arg p A pointer previously retuned by allocate() - virtual void free(void *p); + void free(void *p); /// Minimum block size is (2^min_order). Must be at least 6. static const unsigned min_order = 6; @@ -52,8 +52,8 @@ protected: /// \returns A detached block of the given order mem_header * pop_free(unsigned order); - uintptr_t m_next; - size_t m_size; + uintptr_t m_start, m_end; + size_t m_blocks; mem_header *m_free[max_order - min_order + 1]; size_t m_allocated_size; diff --git a/src/libraries/kutil/include/kutil/vector.h b/src/libraries/kutil/include/kutil/vector.h index 9a45d9b..18408cf 100644 --- a/src/libraries/kutil/include/kutil/vector.h +++ b/src/libraries/kutil/include/kutil/vector.h @@ -15,6 +15,7 @@ class vector { using count_t = S; static constexpr count_t min_capacity = 4; + static constexpr count_t cap_mask = static_cast(-1) >> 1; public: /// Default constructor. Creates an empty vector with no capacity. @@ -60,7 +61,7 @@ public: /// static storage. vector(T *data, count_t size, count_t capacity) : m_size(size), - m_capacity(capacity), + m_capacity(capacity | ~cap_mask), m_elements(&data[0]) { } @@ -69,13 +70,19 @@ public: ~vector() { while (m_size) remove(); - kfree(m_elements); + + bool was_static = m_capacity & ~cap_mask; + if (!was_static) + kfree(m_elements); } /// Get the size of the array. - /// \returns The number of elements in the array inline count_t count() const { return m_size; } + /// Get the capacity of the array. This is the amount of space + /// actually allocated. + inline count_t capacity() const { return m_capacity & cap_mask; } + /// Access an element in the array. inline T & operator[] (count_t i) { return m_elements[i]; } @@ -83,19 +90,15 @@ public: inline const T & operator[] (count_t i) const { return m_elements[i]; } /// Get a pointer to the beginning for iteration. - /// \returns A pointer to the beginning of the array T * begin() { return m_elements; } /// Get a pointer to the beginning for iteration. - /// \returns A pointer to the beginning of the array const T * begin() const { return m_elements; } /// Get a pointer to the end for iteration. - /// \returns A pointer to the end of the array T * end() { return m_elements + m_size; } /// Get a pointer to the end for iteration. - /// \returns A pointer to the end of the array const T * end() const { return m_elements + m_size; } /// Add an item onto the array by copying it. @@ -255,7 +258,7 @@ public: /// \arg size Size of the array void ensure_capacity(count_t size) { - if (m_capacity >= size) return; + if (capacity() >= size) return; count_t capacity = (1 << log2(size)); if (capacity < min_capacity) capacity = min_capacity; @@ -267,6 +270,7 @@ public: /// \arg capacity Number of elements to allocate void set_capacity(count_t capacity) { + bool was_static = m_capacity & ~cap_mask; T *new_array = reinterpret_cast(kalloc(capacity * sizeof(T))); count_t size = capacity > m_size ? m_size : capacity; @@ -276,7 +280,8 @@ public: m_size = size; m_capacity = capacity; - kfree(m_elements); + if (!was_static) + kfree(m_elements); m_elements = new_array; }