From 4252e90680a6c7591cf7bfc330e119c3952f4553 Mon Sep 17 00:00:00 2001 From: StoneDC <19905433+StoneDC@users.noreply.github.com> Date: Thu, 29 Oct 2026 19:32:29 +0800 Subject: [PATCH] test(libc): verify bounded copy padding and write extents --- core/libs/prx/libc/CMakeLists.txt | 10 +++ core/libs/tests/GuestStrncpyProtected.cpp | 80 +++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 core/libs/tests/GuestStrncpyProtected.cpp diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index e4c0758f24..c2cde75bd6 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -558,6 +558,16 @@ if(BUILD_TESTING) set_tests_properties(guest_stdio_variadic PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_strncpy_protected_tests ${LIBS_INCLUDE_DIR}/tests/GuestStrncpyProtected.cpp) + target_include_directories(guest_strncpy_protected_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_strncpy_protected_tests PRIVATE libc) + configure_windows_unwind(guest_strncpy_protected_tests) + add_test(NAME guest_strncpy_protected COMMAND guest_strncpy_protected_tests) + set_tests_properties(guest_strncpy_protected PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_strncpy_protected PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_execute_once_tests ${LIBS_INCLUDE_DIR}/tests/GuestExecuteOnce.cpp) target_include_directories(guest_execute_once_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_execute_once_tests PRIVATE libc) diff --git a/core/libs/tests/GuestStrncpyProtected.cpp b/core/libs/tests/GuestStrncpyProtected.cpp new file mode 100644 index 0000000000..390257d386 --- /dev/null +++ b/core/libs/tests/GuestStrncpyProtected.cpp @@ -0,0 +1,80 @@ +#include "prx/libc/include/Api.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void Require(bool condition) { + if (!condition) { + std::fputs("GuestStrncpyProtected check failed\n", stderr); + std::abort(); + } +} + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +class Buffer { + unsigned char* allocation; + std::size_t page; +public: + Buffer() { +#ifdef _WIN32 + SYSTEM_INFO info{}; + GetSystemInfo(&info); + page = info.dwPageSize; + allocation = static_cast(VirtualAlloc(nullptr, page * 3, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS)); + Require(allocation != nullptr); + DWORD previous; + Require(VirtualProtect(allocation + page, page, PAGE_READWRITE, &previous) != 0); +#else + const long size = sysconf(_SC_PAGESIZE); + Require(size > 0); + page = static_cast(size); + allocation = static_cast(mmap(nullptr, page * 3, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + Require(allocation != MAP_FAILED); + Require(mprotect(allocation + page, page, PROT_READ | PROT_WRITE) == 0); +#endif + std::memset(allocation + page, 0xa5, page); + } + ~Buffer() { +#ifdef _WIN32 + Require(VirtualFree(allocation, 0, MEM_RELEASE) != 0); +#else + Require(munmap(allocation, page * 3) == 0); +#endif + } + Buffer(const Buffer&) = delete; + Buffer& operator=(const Buffer&) = delete; + unsigned char* At(std::size_t count) { + Require(count <= page - 32); + return allocation + page * 2 - (std::max)(count, std::size_t{1}); + } +}; + +int main() { + Buffer input, output; + for (std::size_t count = 0; count <= 192; ++count) { + for (std::size_t length : {std::size_t{0}, count / 2, count, count + 1}) { + const std::string original(length, static_cast(0x81)); + auto* source = reinterpret_cast(input.At(length + 1)); + auto* destination = reinterpret_cast(output.At(count)); + std::memcpy(source, original.c_str(), length + 1); + std::memset(destination - 16, 0xa5, (std::max)(count, std::size_t{1}) + 16); + Require(strncpy_nid_postfix(destination, source, count) == destination); + for (std::size_t i = 0; i < count; ++i) Require(destination[i] == (i < length ? static_cast(0x81) : '\0')); + if (count == 0) Require(static_cast(*destination) == 0xa5); + for (int i = 1; i <= 16; ++i) Require(static_cast(destination[-i]) == 0xa5); + Require(std::memcmp(source, original.c_str(), length + 1) == 0); + } + } +}