#include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include extern "C" { char* APS5_VABI basename_nid_postfix(const char*); char* APS5_VABI __inet_ntoa_nid_postfix(unsigned int); char* APS5_VABI dirname_nid_postfix(const char*); int* APS5_VABI __error_nid_postfix(); int APS5_VABI strcasecmp_nid_postfix(const char*, const char*); int APS5_VABI strncasecmp_nid_postfix(const char*, const char*, std::size_t); std::size_t APS5_VABI strnlen_nid_postfix(const char*, std::size_t); std::size_t APS5_VABI strnlen_s_nid_postfix(const char*, std::size_t); char* APS5_VABI strncat_nid_postfix(char*, const char*, std::size_t); char* APS5_VABI strpbrk_nid_postfix(const char*, const char*); std::size_t APS5_VABI strcspn_nid_postfix(const char*, const char*); std::size_t APS5_VABI strlcat_nid_postfix(char*, const char*, std::size_t); char* APS5_VABI stpcpy_nid_postfix(char*, const char*); char* APS5_VABI strtok_r_nid_postfix(char*, const char*, char**); char* APS5_VABI strtok_nid_postfix(char*, const char*); char* APS5_VABI strsep_nid_postfix(char**, const char*); char* APS5_VABI strcasestr_nid_postfix(const char*, const char*); int APS5_VABI strcpy_s_nid_postfix(char*, std::size_t, const char*); int APS5_VABI strcat_s_nid_postfix(char*, std::size_t, const char*); int APS5_VABI strncat_s_nid_postfix(char*, std::size_t, const char*, std::size_t); int APS5_VABI memcpy_s_nid_postfix(void*, std::size_t, const void*, std::size_t); int APS5_VABI memmove_s_nid_postfix(void*, std::size_t, const void*, std::size_t); int APS5_VABI memset_s_nid_postfix(void*, std::size_t, int, std::size_t); char* APS5_VABI strnstr_nid_postfix(const char*, const char*, std::size_t); std::size_t APS5_VABI strxfrm_nid_postfix(char*, const char*, std::size_t); int APS5_VABI snprintf_s_nid_postfix(char*, std::size_t, const char*, ...); int APS5_VABI sscanf_s_nid_postfix(const char*, const char*, ...); int APS5_VABI __inet_aton_nid_postfix(const char*, void*); std::uint32_t APS5_VABI __inet_addr_nid_postfix(const char*); } static void Require(bool condition) { if (!condition) { std::fputs("Guest string check failed\n", stderr); std::abort(); } } static bool CheckMemcpyOverlap() { bool correct = true; for (const auto offset : {0, 1, -1, 3, -3}) { unsigned char bytes[16]; std::memset(bytes, 0x5a, sizeof(bytes)); auto* destination = bytes + 4; const auto* source = destination + offset; const int error = memcpy_s_nid_postfix(destination, 8, source, 4); bool cleared = true; for (unsigned i = 4; i < 12; ++i) cleared &= bytes[i] == 0; const bool matches = error == 22 && cleared && bytes[3] == 0x5a && bytes[12] == 0x5a; if (!matches) std::fprintf(stderr, "memcpy_s overlap %+d: expected EINVAL 22 and eight zero bytes, received %d\n", offset, error); correct &= matches; } unsigned char adjacent[] = {1, 2, 3, 4, 5, 6, 7, 8}; Require(memcpy_s_nid_postfix(adjacent, 4, adjacent + 4, 4) == 0); Require(std::memcmp(adjacent, adjacent + 4, 4) == 0); const unsigned char original[] = {1, 2, 3, 4, 5, 6, 7, 8}; std::memcpy(adjacent, original, sizeof(adjacent)); Require(memcpy_s_nid_postfix(adjacent + 4, 4, adjacent, 4) == 0); const unsigned char unchanged[] = {1, 2, 3, 4, 1, 2, 3, 4}; Require(std::memcmp(adjacent, unchanged, sizeof(adjacent)) == 0); Require(memcpy_s_nid_postfix(adjacent, sizeof(adjacent), adjacent, 0) == 0); Require(std::memcmp(adjacent, unchanged, sizeof(adjacent)) == 0); return correct; } static void CheckSecureCopySizeLimits() { using Copy = int (APS5_VABI*)(void*, std::size_t, const void*, std::size_t); constexpr std::size_t RsizeMax = SIZE_MAX >> 1; const std::array original = {1, 2, 3, 4, 5, 6, 7, 8}; const unsigned char source[] = {9, 10, 11, 12}; for (Copy copy : {memcpy_s_nid_postfix, memmove_s_nid_postfix}) { for (const auto size : {RsizeMax + 1, SIZE_MAX}) { for (const auto count : {std::size_t{1}, std::size_t{0}, RsizeMax + 1, SIZE_MAX}) { for (const void* input : {static_cast(source), static_cast(nullptr)}) { auto bytes = original; Require(copy(bytes.data() + 2, size, input, count) != 0); Require(bytes == original); } } } for (const auto count : {RsizeMax + 1, SIZE_MAX}) { auto bytes = original; Require(copy(bytes.data() + 2, 4, source, count) != 0); const std::array cleared = {1, 2, 0, 0, 0, 0, 7, 8}; Require(bytes == cleared); } auto bytes = original; Require(copy(bytes.data() + 2, RsizeMax, source, 1) == 0); const std::array copied = {1, 2, 9, 4, 5, 6, 7, 8}; Require(bytes == copied); Require(copy(bytes.data() + 2, 4, source, 0) == 0 && bytes == copied); Require(copy(nullptr, SIZE_MAX, source, 1) == 22); bytes = original; Require(copy(bytes.data() + 2, 4, nullptr, 1) == 22); const std::array cleared = {1, 2, 0, 0, 0, 0, 7, 8}; Require(bytes == cleared); } } static void CheckBoundsCheckedFunctions() { char small[4] = "zz"; Require(strcpy_s_nid_postfix(small, sizeof(small), "abc") == 0 && std::strcmp(small, "abc") == 0); Require(strcpy_s_nid_postfix(small, sizeof(small), "abcd") == 34 && small[0] == '\0'); Require(strcpy_s_nid_postfix(nullptr, 4, "a") == 22); char joined[8] = "ab"; Require(strcat_s_nid_postfix(joined, sizeof(joined), "cd") == 0 && std::strcmp(joined, "abcd") == 0); Require(strncat_s_nid_postfix(joined, sizeof(joined), "efgh", 2) == 0 && std::strcmp(joined, "abcdef") == 0); Require(strcat_s_nid_postfix(joined, sizeof(joined), "gh") == 34 && joined[0] == '\0'); char bytes[4] = {1, 2, 3, 4}; const char source[4] = {5, 6, 7, 8}; Require(memcpy_s_nid_postfix(bytes, sizeof(bytes), source, 2) == 0 && bytes[0] == 5 && bytes[2] == 3); Require(memcpy_s_nid_postfix(bytes, 2, source, 4) == 34 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 3); char overlap[6] = "abcde"; Require(memmove_s_nid_postfix(overlap + 1, 5, overlap, 3) == 0 && std::strcmp(overlap, "aabce") == 0); Require(memset_s_nid_postfix(bytes, sizeof(bytes), 9, 8) == 22 && bytes[0] == 9 && bytes[3] == 9); char sentinel[4] = {1, 2, 3, 4}; Require(memset_s_nid_postfix(sentinel, sizeof(sentinel), 7, 2) == 0 && sentinel[1] == 7 && sentinel[2] == 3); Require(memset_s_nid_postfix(sentinel, (SIZE_MAX >> 1) + 1, 0, 1) == 22 && sentinel[0] == 7); Require(memset_s_nid_postfix(sentinel, sizeof(sentinel), 0, (SIZE_MAX >> 1) + 1) == 22 && sentinel[0] == 7 && sentinel[3] == 4); Require(memset_s_nid_postfix(sentinel, SIZE_MAX >> 1, 5, 1) == 0 && sentinel[0] == 5 && sentinel[1] == 7); Require(memset_s_nid_postfix(nullptr, sizeof(sentinel), 0, 1) == 22); Require(strnstr_nid_postfix("haystack", "st", 4) == nullptr); const char haystack[] = "haystack"; Require(strnstr_nid_postfix(haystack, "st", 6) == haystack + 3); char formatted[8]; Require(snprintf_s_nid_postfix(formatted, sizeof(formatted), "%d-%s", 42, "x") == 4 && std::strcmp(formatted, "42-x") == 0); } static void CheckCaseInsensitiveComparison() { Require(strcasecmp_nid_postfix("", "") == 0 && strcasecmp_nid_postfix("AbC", "aBc") == 0); Require(strcasecmp_nid_postfix("A", "b") == 'a' - 'b' && strcasecmp_nid_postfix("b", "A") == 'b' - 'a'); Require(strcasecmp_nid_postfix("ab", "A") == 'b' && strcasecmp_nid_postfix("AB", "a") == 'b'); Require(strcasecmp_nid_postfix("A", "aB") == -'b' && strcasecmp_nid_postfix("a", "AB") == -'b'); Require(strcasecmp_nid_postfix("", "Z") == -'z' && strcasecmp_nid_postfix("Z", "") == 'z'); Require(strncasecmp_nid_postfix("abc", "ABD", 2) == 0 && strncasecmp_nid_postfix("abc", "ABD", 3) == 'c' - 'd'); Require(strncasecmp_nid_postfix("Ab", "a", 2) == 'b' && strncasecmp_nid_postfix("a", "AB", 5) == -'b'); Require(strncasecmp_nid_postfix("Q", "", 1) == 'q' && strncasecmp_nid_postfix("", "Q", 1) == -'q'); Require(strncasecmp_nid_postfix("x", "y", 0) == 0 && strncasecmp_nid_postfix("same", "SAME", 99) == 0); } static void CheckSscanfS() { #ifndef _WIN32 int number = 0; char word[4] = "zz"; char letter = 0; char value[8] = {}; Require(sscanf_s_nid_postfix(" 12 abc x", "%d %s %c", &number, word, 4u, &letter, 1u) == 3 && number == 12 && std::strcmp(word, "abc") == 0 && letter == 'x'); Require(sscanf_s_nid_postfix("12 abcd", "%d %s", &number, word, 4u) == 1 && word[0] == '\0'); Require(sscanf_s_nid_postfix("key=val", "%3[a-z]=%3s", word, 4u, value, 8u) == 2 && std::strcmp(word, "key") == 0 && std::strcmp(value, "val") == 0); Require(sscanf_s_nid_postfix("abcdef", "%3s", word, 4u) == 1 && std::strcmp(word, "abc") == 0); Require(sscanf_s_nid_postfix("abcdef", "%3s", value, 8u) == 1 && std::strcmp(value, "abc") == 0); Require(sscanf_s_nid_postfix("abcdef", "%3[a-z]", value, 8u) == 1 && std::strcmp(value, "abc") == 0); Require(sscanf_s_nid_postfix("2024ABCD 7", "%4s%s", word, 4u, value, 8u) == 0 && word[0] == '\0'); Require(sscanf_s_nid_postfix("2024ABCD", "%3s%4s", word, 4u, value, 8u) == 2 && std::strcmp(word, "202") == 0 && std::strcmp(value, "4ABC") == 0); int position = 0; Require(sscanf_s_nid_postfix("7 %", "%d %%%n", &number, &position) == 1 && position == 3); Require(sscanf_s_nid_postfix(" ", "%d", &number) == EOF); Require(sscanf_s_nid_postfix("x", "%d", &number) == 0); #endif } static bool CheckDirnameLengths() { const std::string longLeaf(4096, 'x'); const std::string longestParent(1023, 'p'); const std::string tooLongParent(1024, 'q'); struct Case { std::string path; const char* expected; }; const Case cases[] = { {std::string(1023, 'x'), "."}, {std::string(1024, 'x'), "."}, {longLeaf, "."}, {"/" + longLeaf, "/"}, {"one/" + longLeaf, "one"}, {"/one/" + longLeaf, "/one"}, {"one//" + longLeaf + "///", "one"}, {"/one//" + longLeaf + "///", "/one"}, {longLeaf + "///", "."}, {std::string(4096, '/'), "/"}, {longestParent + "/leaf", longestParent.c_str()}, {longestParent + "//leaf///", longestParent.c_str()}, {longestParent + "/", "."}, {tooLongParent + "/leaf", nullptr}, {tooLongParent + "//leaf///", nullptr}, {longLeaf + "/leaf", nullptr}, }; bool passed = true; for (const auto& test : cases) { const std::string original = test.path; *__error_nid_postfix() = 77; const char* result = dirname_nid_postfix(test.path.c_str()); const bool matches = test.expected ? result != nullptr && std::strcmp(result, test.expected) == 0 && *__error_nid_postfix() == 77 : result == nullptr && *__error_nid_postfix() == 63; if (!matches || test.path != original) { std::fprintf(stderr, "dirname input length %zu: expected output length %zu, received %s, errno %d\n", test.path.size(), test.expected ? std::strlen(test.expected) : 0, result ? "a directory" : "null", *__error_nid_postfix()); passed = false; } } return passed; } int main() { if (!CheckDirnameLengths()) return 1; CheckBoundsCheckedFunctions(); CheckCaseInsensitiveComparison(); CheckSscanfS(); Require(std::strcmp(dirname_nid_postfix(nullptr), ".") == 0); char emptyPath[] = ""; Require(std::strcmp(dirname_nid_postfix(emptyPath), ".") == 0); Require(std::strcmp(dirname_nid_postfix("file"), ".") == 0); const char nested[] = "one/two///"; Require(std::strcmp(dirname_nid_postfix(nested), "one") == 0 && std::strcmp(nested, "one/two///") == 0); char repeatedSeparators[] = "one//two"; Require(std::strcmp(dirname_nid_postfix(repeatedSeparators), "one") == 0); char absoluteRepeatedSeparators[] = "/one//two"; Require(std::strcmp(dirname_nid_postfix(absoluteRepeatedSeparators), "/one") == 0); char rootPath[] = "/"; Require(std::strcmp(dirname_nid_postfix(rootPath), "/") == 0); char trailingSlash[] = "foo/"; Require(std::strcmp(dirname_nid_postfix(trailingSlash), ".") == 0); char absolute[] = "/one/two"; Require(std::strcmp(dirname_nid_postfix(absolute), "/one") == 0); char rootChild[] = "/file"; Require(std::strcmp(dirname_nid_postfix(rootChild), "/") == 0); char onlySlashes[] = "////"; Require(std::strcmp(dirname_nid_postfix(onlySlashes), "/") == 0); Require(std::strcmp(basename_nid_postfix(nullptr), ".") == 0); Require(std::strcmp(basename_nid_postfix(""), ".") == 0); Require(std::strcmp(basename_nid_postfix("////"), "/") == 0); const char path[] = "/one/two///"; Require(std::strcmp(basename_nid_postfix(path), "two") == 0); Require(std::strcmp(path, "/one/two///") == 0); Require(std::strcmp(basename_nid_postfix("one\\two"), "one\\two") == 0); const std::string longName(1024, 'x'); Require(basename_nid_postfix(longName.c_str()) == nullptr && *__error_nid_postfix() == 63); const unsigned char loopbackBytes[4] = {127, 0, 0, 1}; unsigned int loopback; std::memcpy(&loopback, loopbackBytes, sizeof(loopback)); char* const dotted = __inet_ntoa_nid_postfix(loopback); Require(std::strcmp(dotted, "127.0.0.1") == 0); Require(__inet_ntoa_nid_postfix(0xffffffffu) == dotted && std::strcmp(dotted, "255.255.255.255") == 0); Require(std::strcmp(__inet_ntoa_nid_postfix(0), "0.0.0.0") == 0); const unsigned char mixedBytes[4] = {10, 200, 3, 45}; unsigned int mixed; std::memcpy(&mixed, mixedBytes, sizeof(mixed)); Require(std::strcmp(__inet_ntoa_nid_postfix(mixed), "10.200.3.45") == 0); const unsigned char paddingBytes[4] = {100, 9, 99, 0}; unsigned int padding; std::memcpy(&padding, paddingBytes, sizeof(padding)); Require(std::strcmp(__inet_ntoa_nid_postfix(padding), "100.9.99.0") == 0); const char bounded[] = {'a', 'b', 'c'}; Require(strnlen_nid_postfix(bounded, 0) == 0); Require(strnlen_nid_postfix(bounded, sizeof(bounded)) == 3); Require(strnlen_nid_postfix("a", 8) == 1); Require(strnlen_s_nid_postfix(nullptr, 8) == 0); Require(strnlen_s_nid_postfix("abc", 8) == 3 && strnlen_s_nid_postfix("abcdef", 4) == 4); char truncated[] = "abXX"; Require(strlcat_nid_postfix(truncated, "cd", 2) == 4); Require(std::strcmp(truncated, "abXX") == 0); char buffer[8] = "ab"; Require(strlcat_nid_postfix(buffer, "cdefgh", sizeof(buffer)) == 8); Require(std::strcmp(buffer, "abcdefg") == 0); Require(strlcat_nid_postfix(buffer, "xyz", 0) == 3); buffer[0] = '\0'; Require(strlcat_nid_postfix(buffer, "x", 1) == 1 && buffer[0] == '\0'); char chained[8] = "zzzzzzz"; char* end = stpcpy_nid_postfix(chained, "ab"); Require(end == chained + 2 && *end == '\0' && chained[3] == 'z'); end = stpcpy_nid_postfix(end, "cd"); Require(end == chained + 4 && std::strcmp(chained, "abcd") == 0); Require(stpcpy_nid_postfix(end, "") == end && chained[5] == 'z'); Require(strncat_nid_postfix(buffer, "xyz", 2) == buffer); Require(std::strcmp(buffer, "xy") == 0); char transformed[8] = "zzzzzzz"; Require(strxfrm_nid_postfix(transformed, "abc", sizeof(transformed)) == 3); Require(std::strcmp(transformed, "abc") == 0 && transformed[4] == 'z'); std::memset(transformed, 'z', sizeof(transformed)); Require(strxfrm_nid_postfix(transformed, "abcdefgh", 4) == 8); Require(std::strcmp(transformed, "abc") == 0 && transformed[4] == 'z'); std::memset(transformed, 'z', sizeof(transformed)); Require(strxfrm_nid_postfix(transformed, "abcdefg", sizeof(transformed)) == 7 && std::strcmp(transformed, "abcdefg") == 0); std::memset(transformed, 'z', sizeof(transformed)); Require(strxfrm_nid_postfix(transformed, "abcdefgh", sizeof(transformed)) == 8 && std::strcmp(transformed, "abcdefg") == 0); std::memset(transformed, 'z', sizeof(transformed)); Require(strxfrm_nid_postfix(transformed, "", sizeof(transformed)) == 0 && transformed[0] == '\0' && transformed[1] == 'z'); Require(strxfrm_nid_postfix(transformed, "abc", 1) == 3 && transformed[0] == '\0' && transformed[1] == 'z'); Require(strxfrm_nid_postfix(nullptr, "abc", 0) == 3); Require(strpbrk_nid_postfix(buffer, "ay") == buffer + 1); Require(strpbrk_nid_postfix(buffer, "") == nullptr); Require(strcspn_nid_postfix(buffer, "y") == 1); const auto inetAton = [](const char* text, const char* expected) { unsigned char address[4] = {0xA5, 0xA5, 0xA5, 0xA5}; if (__inet_aton_nid_postfix(text, address) != 1) return false; char formatted[16]; std::snprintf(formatted, sizeof(formatted), "%u.%u.%u.%u", address[0], address[1], address[2], address[3]); return std::strcmp(formatted, expected) == 0; }; Require(inetAton("192.0.2.42", "192.0.2.42")); Require(inetAton("10.1.2", "10.1.0.2")); Require(inetAton("127.1", "127.0.0.1")); Require(inetAton("3232235777", "192.168.1.1")); Require(inetAton("0x7f.0.0.0x1", "127.0.0.1")); Require(inetAton("0377.0.0.010", "255.0.0.8")); Require(inetAton("1.2.3.4 trailing", "1.2.3.4")); Require(inetAton("1.2.3.4\n", "1.2.3.4")); Require(__inet_aton_nid_postfix("1.2.3.4", nullptr) == 1); for (const char* invalid : {"", " 1.2.3.4", "1.2.3.4.5", "256.1.1.1", "1.2.3.256", "1.2.65536", "08", "1..2", "a.b.c.d", "1.2.3.4x", "0x", "1.2.3.", "-1"}) { unsigned char address[4] = {0xA5, 0xA5, 0xA5, 0xA5}; Require(__inet_aton_nid_postfix(invalid, address) == 0); Require(address[0] == 0xA5 && address[3] == 0xA5); Require(__inet_addr_nid_postfix(invalid) == 0xffffffff); } const auto inetAddr = [](const char* text, std::array expected) { const std::uint32_t value = __inet_addr_nid_postfix(text); std::array bytes{}; std::memcpy(bytes.data(), &value, sizeof(value)); return bytes == expected; }; Require(inetAddr("192.0.2.42", {192, 0, 2, 42})); Require(inetAddr("127.1", {127, 0, 0, 1})); Require(inetAddr("0x7f.0.0.0x1", {127, 0, 0, 1})); Require(inetAddr("0.0.0.0", {0, 0, 0, 0})); Require(inetAddr("255.255.255.255", {255, 255, 255, 255})); Require(inetAddr("4294967296", {0, 0, 0, 0})); Require(inetAddr("18446744073709551617", {0, 0, 0, 1})); char first[] = ",a,,b,"; char second[] = "x:y"; char* firstState = nullptr; char* secondState = nullptr; Require(std::strcmp(strtok_r_nid_postfix(first, ",", &firstState), "a") == 0); Require(std::strcmp(strtok_r_nid_postfix(second, ":", &secondState), "x") == 0); Require(std::strcmp(strtok_r_nid_postfix(nullptr, ",", &firstState), "b") == 0); Require(strtok_r_nid_postfix(nullptr, ",", &firstState) == nullptr); Require(strtok_r_nid_postfix(nullptr, ",", &firstState) == nullptr); Require(std::strcmp(strtok_r_nid_postfix(nullptr, "", &secondState), "y") == 0); char hostTokens[] = "host:next"; Require(std::strcmp(std::strtok(hostTokens, ":"), "host") == 0); char guestTokens[] = ",one,,two:three"; Require(std::strcmp(strtok_nid_postfix(guestTokens, ","), "one") == 0); Require(std::strcmp(strtok_nid_postfix(nullptr, ":,"), "two") == 0); Require(std::strcmp(strtok_nid_postfix(nullptr, ""), "three") == 0); Require(strtok_nid_postfix(nullptr, ",") == nullptr); Require(strtok_nid_postfix(nullptr, ",") == nullptr); Require(std::strcmp(std::strtok(nullptr, ":"), "next") == 0); char fields[] = ",a,,b;c"; char* cursor = fields; Require(strsep_nid_postfix(&cursor, ",;") == fields && fields[0] == '\0' && cursor == fields + 1); Require(std::strcmp(strsep_nid_postfix(&cursor, ",;"), "a") == 0); Require(std::strcmp(strsep_nid_postfix(&cursor, ",;"), "") == 0); Require(std::strcmp(strsep_nid_postfix(&cursor, ",;"), "b") == 0); char* last = cursor; Require(strsep_nid_postfix(&cursor, ",;") == last && std::strcmp(last, "c") == 0 && cursor == nullptr); Require(strsep_nid_postfix(&cursor, ",;") == nullptr && cursor == nullptr); char whole[] = "a,b"; cursor = whole; Require(strsep_nid_postfix(&cursor, "") == whole && std::strcmp(whole, "a,b") == 0 && cursor == nullptr); char empty[] = ""; cursor = empty; Require(strsep_nid_postfix(&cursor, ",") == empty && cursor == nullptr); const char text[] = "aABAbC"; Require(strcasestr_nid_postfix(text, "ababc") == text + 1); Require(strcasestr_nid_postfix(text, "") == text); Require(strcasestr_nid_postfix(text, "abcdef") == nullptr); Require(strcasestr_nid_postfix("", "a") == nullptr); const char highBytes[] = {static_cast(0xff), 'A', 0}; Require(strcasestr_nid_postfix(highBytes, "a") == highBytes + 1); CheckSecureCopySizeLimits(); return CheckMemcpyOverlap() ? 0 : 1; }