From 68a5800c856e8d44158aa5a67b9ddda447bbfd72 Mon Sep 17 00:00:00 2001 From: 3fault Date: Wed, 2 Jul 2025 12:09:21 -0400 Subject: [PATCH] Add test for guard page in test-sample, formatting fix --- src/samples/test-sample/test.cpp | 96 +++++++++++++++++++ .../memory_permission_ext.hpp | 3 +- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index 6b5cdba6..a08be686 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -721,6 +721,101 @@ namespace SleepEx(1, TRUE); return executions == 2; } + + INT32 test_guard_page_seh_filter(LPVOID address, DWORD code, struct _EXCEPTION_POINTERS* ep) + { + // We are only looking for guard page exceptions. + if (code != STATUS_GUARD_PAGE_VIOLATION) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The number of defined elements in the ExceptionInformation array for + // a guard page violation should be 2. + if (ep->ExceptionRecord->NumberParameters != 2) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The ExceptionInformation array specifies additional arguments that + // describe the exception. + auto *exception_information = ep->ExceptionRecord->ExceptionInformation; + + // If this value is zero, the thread attempted to read the inaccessible + // data. If this value is 1, the thread attempted to write to an + // inaccessible address. + if (exception_information[0] != 1) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // The second array element specifies the virtual address of the + // inaccessible data. + if (exception_information[1] != (ULONG_PTR)address) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; + } + + bool test_guard_page() + { + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + + // Allocate a guarded memory region with the length of the system page + // size. + auto *addr = static_cast(VirtualAlloc( + nullptr, + sys_info.dwPageSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE | PAGE_GUARD + )); + if (addr == nullptr) + { + puts("Failed to allocate guard page"); + return false; + } + + bool success = false; + + // We want to access some arbitrary offset into the guarded page, to + // ensure that ExceptionInformation correctly contains the virtual + // address of the inaccessible data, not the base address of the region. + constexpr size_t offset = 10; + + // Trigger a guard page violation + __try + { + addr[offset] = 255; + } + // If the filter function returns EXCEPTION_CONTINUE_SEARCH, the + // exception contains all of the correct information. + __except(test_guard_page_seh_filter( + addr + offset, + GetExceptionCode(), + GetExceptionInformation())) + { + success = true; + } + + // The page guard should be lifted, so no exception should be raised. + __try { + // The previous write should not have went through, this is probably + // superflous. + if (addr[offset] == 255) { + success = false; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + puts("Failed to read from page after guard exception!"); + success = false; + } + + return success; + } } #define RUN_TEST(func, name) \ @@ -761,6 +856,7 @@ int main(const int argc, const char* argv[]) RUN_TEST(test_tls, "TLS") RUN_TEST(test_socket, "Socket") RUN_TEST(test_apc, "APC") + RUN_TEST(test_guard_page, "Guard Page") return valid ? 0 : 1; } diff --git a/src/windows-emulator/memory_permission_ext.hpp b/src/windows-emulator/memory_permission_ext.hpp index ca410475..257acfc6 100644 --- a/src/windows-emulator/memory_permission_ext.hpp +++ b/src/windows-emulator/memory_permission_ext.hpp @@ -75,7 +75,8 @@ struct nt_memory_permission return *this; } - constexpr bool is_guarded() const { + constexpr bool is_guarded() const + { return (this->extended & memory_permission_ext::guard) == memory_permission_ext::guard; } };