#include "prx/libc/include/Api.hpp" #include #include #include #include #include #include #include #include #include static void Require(bool condition) { if (!condition) { std::fputs("GuestWideTokenBoundary 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, delimiters, other; auto* set = delimiters.At(3); set[0] = 0x100; set[1] = 0xd800; set[2] = 0; const std::u16string separator(set, 2); for (std::size_t length = 0; length <= 128; ++length) { auto* source = input.At(length + 1); const std::array values{0x100, 1, 0x8000, 0xd800, 0xffff, 0x100, 0xdc00}; for (std::size_t i = 0; i < length; ++i) source[i] = values[(i + length) % values.size()]; source[length] = 0; const std::u16string saved(source, length + 1); std::vector expected; std::u16string field; for (std::size_t i = 0; i < length; ++i) { if (separator.find(saved[i]) != std::u16string::npos) { if (!field.empty()) { expected.push_back(field); field.clear(); } } else field.push_back(saved[i]); } if (!field.empty()) expected.push_back(field); auto* alternate = other.At(4); alternate[0] = 7; alternate[1] = 0x100; alternate[2] = 9; alternate[3] = 0; char16_t* state = nullptr; char16_t* otherState = nullptr; Require(wcstok_nid_postfix(alternate, set, &otherState) == alternate && alternate[0] == 7 && alternate[1] == 0); for (std::size_t i = 0; i < expected.size(); ++i) { auto* token = wcstok_nid_postfix(i == 0 ? source : nullptr, set, &state); Require(token != nullptr && std::u16string(token) == expected[i]); } Require(wcstok_nid_postfix(expected.empty() ? source : nullptr, set, &state) == nullptr); Require(wcstok_nid_postfix(nullptr, set, &state) == nullptr); auto* last = wcstok_nid_postfix(nullptr, set, &otherState); Require(last == alternate + 2 && last[0] == 9 && last[1] == 0); for (std::size_t i = 0; i <= length; ++i) if (separator.find(saved[i]) == std::u16string::npos) Require(source[i] == saved[i]); Require(set[0] == 0x100 && set[1] == 0xd800 && set[2] == 0); } }