diff --git a/src/kernel/debugcon.cpp b/src/kernel/debugcon.cpp index f6e6827..8167555 100644 --- a/src/kernel/debugcon.cpp +++ b/src/kernel/debugcon.cpp @@ -26,6 +26,20 @@ namespace { } } // anon namespace +void +write(const char *fmt, ...) +{ + char buffer[256]; + + va_list va; + va_start(va, fmt); + size_t n = util::vformat({buffer, sizeof(buffer)}, fmt, va); + va_end(va); + + debug_out(buffer, n); + debug_out("\r\n", 2); +} + void output(j6_log_entry *entry) { diff --git a/src/kernel/debugcon.h b/src/kernel/debugcon.h index 3c2cd0f..13a0393 100644 --- a/src/kernel/debugcon.h +++ b/src/kernel/debugcon.h @@ -16,6 +16,7 @@ static constexpr bool enable = static constexpr uint16_t port = 0x6600; void logger_task(); +void write(const char *fmt, ...); inline void init_logger() diff --git a/src/kernel/frame_allocator.cpp b/src/kernel/frame_allocator.cpp index 9226ac2..9b40e6f 100644 --- a/src/kernel/frame_allocator.cpp +++ b/src/kernel/frame_allocator.cpp @@ -1,6 +1,7 @@ #include #include "assert.h" +#include "debugcon.h" #include "frame_allocator.h" #include "logger.h" #include "memory.h" @@ -51,7 +52,7 @@ frame_allocator::allocate(size_t count, uintptr_t *address) unsigned frame = (o1 << 12) + (o2 << 6) + o3; // See how many contiguous pages are here - unsigned n = bsf(~m3 >> o3); + unsigned n = bsf(~(m3 >> o3)); if (n > count) n = count; @@ -71,6 +72,7 @@ frame_allocator::allocate(size_t count, uintptr_t *address) } } + //debugcon::write("Allocated %2d frames at %016lx - %016lx", n, *address, *address + n * frame_size); return n; } @@ -88,6 +90,8 @@ frame_allocator::free(uintptr_t address, size_t count) if (!count) return; + //debugcon::write("Freeing %2d frames at %016lx - %016lx", count, address, address + count * frame_size); + for (long i = 0; i < m_count; ++i) { frame_block &block = m_blocks[i]; uintptr_t end = block.base + block.count * frame_size; diff --git a/src/kernel/heap_allocator.cpp b/src/kernel/heap_allocator.cpp index 5622135..0ef9465 100644 --- a/src/kernel/heap_allocator.cpp +++ b/src/kernel/heap_allocator.cpp @@ -8,6 +8,8 @@ #include "assert.h" #include "heap_allocator.h" #include "memory.h" +#include "objects/vm_area.h" +#include "vm_space.h" uint32_t & get_map_key(heap_allocator::block_info &info) { return info.offset; } @@ -46,7 +48,6 @@ heap_allocator::heap_allocator(uintptr_t start, size_t size, uintptr_t heapmap) m_maxsize {size}, m_allocated_size {0}, m_map (reinterpret_cast(heapmap), 512) - { memset(m_free, 0, sizeof(m_free)); } @@ -57,6 +58,11 @@ heap_allocator::allocate(size_t length) if (length == 0) return nullptr; + static constexpr unsigned min_order = + __debug_heap_allocation ? + 12 : // allocating full pages in debug mode + heap_allocator::min_order; + unsigned order = util::log2(length); if (order < min_order) order = min_order; @@ -98,6 +104,15 @@ heap_allocator::free(void *p) size_t size = (1ull << info->order); m_allocated_size -= size; + if constexpr (__debug_heap_allocation) { + extern obj::vm_area_untracked &g_kernel_heap_area; + + size_t offset = reinterpret_cast(p) - mem::heap_offset; + size_t pages = mem::bytes_to_pages(size); + vm_space::kernel_space().lock(g_kernel_heap_area, offset, pages); + return; + } + block->clear(info->order); block = merge_block(block); register_free_block(block, block->order); diff --git a/src/kernel/heap_allocator.h b/src/kernel/heap_allocator.h index 70daa6f..d77f5f7 100644 --- a/src/kernel/heap_allocator.h +++ b/src/kernel/heap_allocator.h @@ -34,8 +34,9 @@ public: /// allocation with the contents copied over. void * reallocate(void *p, size_t old_length, size_t new_length); - /// Minimum block size is (2^min_order). Must be at least 6. - static const unsigned min_order = 6; // 2^6 == 64 B + /// Minimum block size is (2^min_order). Must be at least 5 in + /// order to hold a free_header. + static const unsigned min_order = 5; // 2^5 == 32 B /// Maximum block size is (2^max_order). Must be less than 32 + min_order. static const unsigned max_order = 22; // 2^22 == 4 MiB diff --git a/src/kernel/memory.h.cog b/src/kernel/memory.h.cog index bd2a210..14d9e81 100644 --- a/src/kernel/memory.h.cog +++ b/src/kernel/memory.h.cog @@ -88,3 +88,6 @@ constexpr uintptr_t page_align_up(uintptr_t a) { return page_align_down(a-1) + f void initialize(bootproto::args &args); } // namespace mem + +static constexpr bool __debug_heap_allocation = false; + diff --git a/src/kernel/objects/kobject.cpp b/src/kernel/objects/kobject.cpp index f3e4b1c..fcb5f4d 100644 --- a/src/kernel/objects/kobject.cpp +++ b/src/kernel/objects/kobject.cpp @@ -2,6 +2,7 @@ #include #include "assert.h" +#include "logger.h" #include "objects/kobject.h" #include "objects/thread.h" @@ -14,6 +15,13 @@ static uint32_t next_oids [types_count] = { 0 }; static_assert(types_count <= (1 << kobject::koid_type_bits), "kobject::koid_type_bits cannot represent all kobject types"); +static const char *type_names[] = { +#define OBJECT_TYPE( name, val ) #name , +#include +#undef OBJECT_TYPE + nullptr +}; + static uint32_t oid_generate(kobject::type t) { @@ -26,13 +34,25 @@ kobject::kobject(type t) : m_handle_count {0}, m_type {t}, m_obj_id {oid_generate(t)} -{} +{ + log::spam(logs::objs, "%s[%02lx] created @ 0x%lx", type_name(m_type), m_obj_id, this); +} -kobject::~kobject() {} +kobject::~kobject() +{ + log::spam(logs::objs, "%s[%02lx] deleted", type_name(m_type), m_obj_id); +} + +const char * +kobject::type_name(type t) +{ + return type_names[static_cast(t)]; +} void kobject::on_no_handles() { + log::verbose(logs::objs, "Deleting %s[%02lx] on no handles", type_name(m_type), m_obj_id); delete this; } diff --git a/src/kernel/objects/kobject.h b/src/kernel/objects/kobject.h index 93bdfaa..5583cc0 100644 --- a/src/kernel/objects/kobject.h +++ b/src/kernel/objects/kobject.h @@ -38,6 +38,8 @@ public: return static_cast((koid >> koid_type_shift) & koid_type_mask); } + static const char * type_name(type t); + /// Get this object's type inline type get_type() const { return m_type; } diff --git a/src/kernel/page_table.cpp b/src/kernel/page_table.cpp index ce4d822..dff83e0 100644 --- a/src/kernel/page_table.cpp +++ b/src/kernel/page_table.cpp @@ -171,7 +171,13 @@ page_table::set(int i, page_table *p, uint16_t flags) (flags & 0xfff); } -struct free_page_header { free_page_header *next; }; +struct free_page_header +{ + free_page_header *next; + + static constexpr size_t x_count = (frame_size - sizeof(next)) / sizeof(uint64_t); + uint64_t x [x_count]; +}; page_table * page_table::get_table_page() @@ -213,18 +219,14 @@ page_table::fill_table_page_cache() uintptr_t phys = 0; size_t n = fa.allocate(request_pages, &phys); - free_page_header *start = + free_page_header *pages = mem::to_virtual(phys); - for (int i = 0; i < n - 1; ++i) - util::offset_pointer(start, i * frame_size) - ->next = util::offset_pointer(start, (i+1) * frame_size); - - free_page_header *end = - util::offset_pointer(start, (n-1) * frame_size); - - end->next = s_page_cache; - s_page_cache = start; + for (int i = 0; i < n - 1; ++i) { + pages[i].next = &pages[i + 1]; + } + pages[n - 1].next = s_page_cache; + s_page_cache = pages; } void diff --git a/src/kernel/vm_space.cpp b/src/kernel/vm_space.cpp index f123506..60d8303 100644 --- a/src/kernel/vm_space.cpp +++ b/src/kernel/vm_space.cpp @@ -15,9 +15,11 @@ using obj::vm_flags; // The initial memory for the array of areas for the kernel space -constexpr size_t num_kernel_areas = 8; +static constexpr size_t num_kernel_areas = 8; static uint64_t kernel_areas[num_kernel_areas * 2]; +static constexpr uint64_t locked_page_tag = 0xbadfe11a; + int vm_space::area::compare(const vm_space::area &o) const { @@ -115,7 +117,7 @@ vm_space::can_resize(const obj::vm_area &vma, size_t size) const { uintptr_t base = 0; unsigned n = m_areas.count(); - for (unsigned i = 0; i < n - 1; ++i) { + for (unsigned i = 1; i < n - 1; ++i) { const area &prev = m_areas[i - 1]; if (prev.area != &vma) continue; @@ -251,6 +253,38 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr fa.free(free_start, free_count); } +void +vm_space::lock(const obj::vm_area &vma, uintptr_t offset, size_t count) +{ + using mem::frame_size; + util::scoped_lock lock {m_lock}; + + uintptr_t base = 0; + if (!find_vma(vma, base)) + return; + + uintptr_t addr = base + offset; + + frame_allocator &fa = frame_allocator::get(); + page_table::iterator it {addr, m_pml4}; + + while (count--) { + uint64_t &e = it.entry(page_table::level::pt); + uintptr_t phys = e & ~0xfffull; + + if (e & page_table::flag::present) { + uint64_t orig = e; + e = locked_page_tag; + if (orig & page_table::flag::accessed) { + auto *addr = reinterpret_cast(it.vaddress()); + asm ( "invlpg %0" :: "m"(*addr) : "memory" ); + } + fa.free(phys, 1); + } + ++it; + } +} + uintptr_t vm_space::lookup(const obj::vm_area &vma, uintptr_t offset) { @@ -298,6 +332,14 @@ vm_space::handle_fault(uintptr_t addr, fault_type fault) if (!area) return false; + if constexpr (__debug_heap_allocation) { + page_table::iterator it {addr, m_pml4}; + uint64_t &e = it.entry(page_table::level::pt); + kassert(e != locked_page_tag, "Use-after-free"); + if (e == locked_page_tag) + return false; + } + uintptr_t offset = page - base; uintptr_t phys_page = 0; if (!area->get_page(offset, phys_page)) diff --git a/src/kernel/vm_space.h b/src/kernel/vm_space.h index 9d81401..ef330ad 100644 --- a/src/kernel/vm_space.h +++ b/src/kernel/vm_space.h @@ -65,7 +65,14 @@ public: /// \arg offset Offset of the starting virutal address from the VMA base /// \arg count The number of pages worth of mappings to clear /// \arg free If true, free the pages back to the system - void clear(const obj::vm_area &vma, uintptr_t start, size_t count, bool free = false); + void clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool free = false); + + /// Clear mappings from the given region, and mark it as locked. Used for + /// debugging heap allocation reuse. + /// \arg area The VMA these mappings applies to + /// \arg offset Offset of the starting virutal address from the VMA base + /// \arg count The number of pages worth of mappings to clear + void lock(const obj::vm_area &vma, uintptr_t offset, size_t count); /// Look up the address of a given VMA's offset uintptr_t lookup(const obj::vm_area &vma, uintptr_t offset);