From 1d5d4a7663a22b96e135bc7033a988993523c0d4 Mon Sep 17 00:00:00 2001 From: StoneDC <19905433+StoneDC@users.noreply.github.com> Date: Fri, 30 Oct 2026 20:55:05 +0800 Subject: [PATCH] test(libc): bound UTF-16 memory searches including zero units --- core/libs/prx/libc/CMakeLists.txt | 10 +++ core/libs/tests/GuestWmemchrBoundary.cpp | 85 ++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 core/libs/tests/GuestWmemchrBoundary.cpp diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index 176bc416d1e..95a8609c729 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -1051,6 +1051,16 @@ if(BUILD_TESTING) set_tests_properties(guest_search PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_wmemchr_boundary_tests ${LIBS_INCLUDE_DIR}/tests/GuestWmemchrBoundary.cpp) + target_include_directories(guest_wmemchr_boundary_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_wmemchr_boundary_tests PRIVATE libc) + configure_windows_unwind(guest_wmemchr_boundary_tests) + add_test(NAME guest_wmemchr_boundary COMMAND guest_wmemchr_boundary_tests) + set_tests_properties(guest_wmemchr_boundary PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_wmemchr_boundary PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_demangle_tests ${LIBS_INCLUDE_DIR}/tests/GuestDemangle.cpp) target_include_directories(guest_demangle_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_demangle_tests PRIVATE libc) diff --git a/core/libs/tests/GuestWmemchrBoundary.cpp b/core/libs/tests/GuestWmemchrBoundary.cpp new file mode 100644 index 00000000000..889e7123729 --- /dev/null +++ b/core/libs/tests/GuestWmemchrBoundary.cpp @@ -0,0 +1,85 @@ +#include "prx/libc/include/Api.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void Require(bool condition) { + if (!condition) { + std::fputs("GuestWmemchrBoundary 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; + template + T* At(std::size_t count) { + Require(count <= (page - 32) / sizeof(T)); + return reinterpret_cast(allocation + page * 2 - (std::max)(count, std::size_t{1}) * sizeof(T)); + } +}; + +template +static void Prefix(const T* data) { + for (int index = 1; index <= 8; ++index) Require(data[-index] == static_cast(sizeof(T) == 1 ? 0xa5 : 0xa5a5)); +} + +int main() { + Buffer input; + const std::array values{0, 1, 0xff, 0x100, 0x8000, 0xd800, 0xffff}; + for (std::size_t length = 0; length <= 192; ++length) { + auto* source = input.At(length); + const auto extent = (std::max)(length, std::size_t{1}); + for (std::size_t i = 0; i < extent; ++i) source[i] = values[(i + length) % values.size()]; + const std::vector saved(source, source + extent); + for (char16_t target : {0, 1, 0xff, 0x100, 0x8000, 0xd800, 0xdc00, 0xffff}) { + const auto found = std::find(saved.begin(), saved.begin() + length, target); + const auto* expected = found == saved.begin() + length ? nullptr : source + (found - saved.begin()); + Require(wmemchr_nid_postfix(source, target, length) == expected); + Require(std::equal(saved.begin(), saved.end(), source)); + } + } +}