Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/libs/prx/libc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_strings PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(guest_libc_annexk_rsizemax_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibcAnnexkRsizeMax.cpp)
target_include_directories(guest_libc_annexk_rsizemax_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libc_annexk_rsizemax_tests PRIVATE libc)
configure_windows_unwind(guest_libc_annexk_rsizemax_tests)
add_test(NAME guest_libc_annexk_rsizemax COMMAND guest_libc_annexk_rsizemax_tests)
set_tests_properties(guest_libc_annexk_rsizemax PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_libc_annexk_rsizemax PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(guest_wcsrtombs_tests ${LIBS_INCLUDE_DIR}/tests/GuestWcsrtombs.cpp)
target_include_directories(guest_wcsrtombs_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_wcsrtombs_tests PRIVATE libc)
Expand Down
36 changes: 17 additions & 19 deletions core/libs/prx/libc/src/Strings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

namespace {

constexpr size_t RsizeMax = (size_t{1} << 61) - 1;

int CompareUnits(char16_t left, char16_t right) {
return left < right ? -1 : 1;
}
Expand Down Expand Up @@ -528,9 +530,12 @@ int APS5_VABI strcoll_nid_postfix(const char* s1, const char* s2) {
int APS5_VABI strncpy_s_nid_postfix(char* dest, size_t destsz, const char* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = SIZE_MAX >> 1;
if (!dest || destsz == 0) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
if (count > RsizeMax) {
dest[0] = '\0';
return GuestEinval;
}
if (!src) {
dest[0] = '\0';
return GuestEinval;
Expand All @@ -553,15 +558,14 @@ int APS5_VABI strncpy_s_nid_postfix(char* dest, size_t destsz, const char* src,
}

int APS5_VABI strcpy_s_nid_postfix(char* dest, size_t destsz, const char* src) {
return strncpy_s_nid_postfix(dest, destsz, src, static_cast<size_t>(-1));
return strncpy_s_nid_postfix(dest, destsz, src, RsizeMax);
}

int APS5_VABI strncat_s_nid_postfix(char* dest, size_t destsz, const char* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = SIZE_MAX >> 1;
if (!dest || destsz == 0) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
size_t used = 0;
while (used < destsz && dest[used] != '\0') ++used;
if (used == destsz || !src) {
Expand Down Expand Up @@ -592,9 +596,8 @@ int APS5_VABI strcat_s_nid_postfix(char* dest, size_t destsz, const char* src) {
int APS5_VABI memcpy_s_nid_postfix(void* dest, size_t destsz, const void* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = SIZE_MAX >> 1;
if (!dest) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
if (!src || count > destsz) {
std::memset(dest, 0, destsz);
return src ? GuestErange : GuestEinval;
Expand All @@ -613,9 +616,8 @@ int APS5_VABI memcpy_s_nid_postfix(void* dest, size_t destsz, const void* src, s
int APS5_VABI memmove_s_nid_postfix(void* dest, size_t destsz, const void* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = SIZE_MAX >> 1;
if (!dest) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
if (!src || count > destsz) {
std::memset(dest, 0, destsz);
return src ? GuestErange : GuestEinval;
Expand All @@ -626,8 +628,8 @@ int APS5_VABI memmove_s_nid_postfix(void* dest, size_t destsz, const void* src,

int APS5_VABI memset_s_nid_postfix(void* dest, size_t destsz, int value, size_t count) {
constexpr int GuestEinval = 22;
constexpr size_t RsizeMax = SIZE_MAX >> 1;
if (!dest || destsz > RsizeMax || count > RsizeMax) return GuestEinval;
constexpr size_t MemsetSizeMax = SIZE_MAX >> 1;
if (!dest || destsz > MemsetSizeMax || count > MemsetSizeMax) return GuestEinval;
const auto length = count > destsz ? destsz : count;
auto* bytes = static_cast<volatile unsigned char*>(dest);
for (size_t index = 0; index < length; ++index) bytes[index] = static_cast<unsigned char>(value);
Expand Down Expand Up @@ -849,9 +851,8 @@ char* APS5_VABI strtok_s_nid_postfix(char* str, const char* delim, char** contex
int APS5_VABI wmemcpy_s_nid_postfix(char16_t* dest, size_t destsz, const char16_t* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = (SIZE_MAX >> 1) / sizeof(char16_t);
if (!dest) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
if (!src || count > destsz) {
wmemset_nid_postfix(dest, 0, destsz);

Expand All @@ -872,9 +873,8 @@ int APS5_VABI wmemcpy_s_nid_postfix(char16_t* dest, size_t destsz, const char16_
int APS5_VABI wmemmove_s_nid_postfix(char16_t* dest, size_t destsz, const char16_t* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = (SIZE_MAX >> 1) / sizeof(char16_t);
if (!dest) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
if (!src || count > destsz) {
wmemset_nid_postfix(dest, 0, destsz);

Expand All @@ -887,9 +887,8 @@ int APS5_VABI wmemmove_s_nid_postfix(char16_t* dest, size_t destsz, const char16
int APS5_VABI wcsncpy_s_nid_postfix(char16_t* dest, size_t destsz, const char16_t* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = (SIZE_MAX >> 1) / sizeof(char16_t);
if (!dest || destsz == 0) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
if (!src) {
dest[0] = 0;
return GuestEinval;
Expand Down Expand Up @@ -923,9 +922,8 @@ char16_t* APS5_VABI wcsncat_nid_postfix(char16_t* dest, const char16_t* src, siz
int APS5_VABI wcsncat_s_nid_postfix(char16_t* dest, size_t destsz, const char16_t* src, size_t count) {
constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr size_t RsizeMax = (SIZE_MAX >> 1) / sizeof(char16_t);
if (!dest || destsz == 0) return GuestEinval;
if (destsz > RsizeMax) return GuestErange;
if (destsz > RsizeMax) return GuestEinval;
size_t used = 0;
while (used < destsz && dest[used] != 0) ++used;
if (used == destsz || !src) {
Expand Down
155 changes: 155 additions & 0 deletions core/libs/tests/GuestLibcAnnexkRsizeMax.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include "prx/libc/include/Api.hpp"
#include "prx/libc/include/FileStream.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <string>

namespace {

constexpr int GuestEinval = 22;
constexpr int GuestErange = 34;
constexpr int GuestEexist = 17;
constexpr int GuestEisdir = 21;
constexpr int GuestEnoent = 2;
constexpr std::size_t RsizeMax = 0x1fffffffffffffff;
constexpr unsigned char Poison = 0x5a;

void Check(bool condition, int line) {
if (!condition) {
std::fprintf(stderr, "Annex K size limit check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)

std::string Hex(const void* bytes, std::size_t count) {
const auto* values = static_cast<const unsigned char*>(bytes);
static const char digits[] = "0123456789abcdef";
std::string text;
for (std::size_t index = 0; index < count; ++index) {
text.push_back(digits[values[index] >> 4]);
text.push_back(digits[values[index] & 0x0f]);
}
return text;
}

void CheckMemcpyS() {
unsigned char buffer[64];
const unsigned char source[8] = {1, 2, 3, 4, 5, 6, 7, 8};
const std::size_t accepted[] = {0x40, 0x1000, 0x100000, 0x40000000, 0x7fffffff, 0x80000000, 0xffffffff,
std::size_t{1} << 32, std::size_t{1} << 40, RsizeMax};
for (const auto size : accepted) {
std::memset(buffer, Poison, sizeof(buffer));
const int error = memcpy_s_nid_postfix(buffer, size, source, 1);
Require(error == 0 && buffer[0] == 1);
}
const std::size_t refused[] = {0x2000000000000000, 0x4000000000000000, 0x7ffffffffffffffe, 0x7fffffffffffffff,
0x8000000000000000, SIZE_MAX};
for (const auto size : refused) {
std::memset(buffer, Poison, sizeof(buffer));
const int error = memcpy_s_nid_postfix(buffer, size, source, 1);
Require(error == GuestEinval && buffer[0] != 1);
}
std::memset(buffer, Poison, sizeof(buffer));
Require(memcpy_s_nid_postfix(buffer, 4, source, 8) == GuestErange && Hex(buffer, 8) == "000000005a5a5a5a");
std::memset(buffer, Poison, sizeof(buffer));
Require(memcpy_s_nid_postfix(buffer, 64, buffer + 4, 16) == GuestEinval && Hex(buffer, 8) == "0000000000000000");
Require(memcpy_s_nid_postfix(nullptr, 8, source, 1) == GuestEinval);
std::memset(buffer, Poison, sizeof(buffer));
Require(memcpy_s_nid_postfix(buffer, 8, nullptr, 4) == GuestEinval && Hex(buffer, 8) == "0000000000000000");
Require(memcpy_s_nid_postfix(buffer, 8, nullptr, 0) == GuestEinval);
}

void CheckWmemcpyS() {
char16_t buffer[32];
const char16_t source[4] = {1, 2, 3, 4};
const std::size_t accepted[] = {0x20, 0x100000, 0x7fffffff, 0x80000000, std::size_t{1} << 32, RsizeMax};
for (const auto size : accepted) {
std::memset(buffer, 0xaa, sizeof(buffer));
const int error = wmemcpy_s_nid_postfix(buffer, size, source, 1);
Require(error == 0 && buffer[0] == 1);
}
const std::size_t refused[] = {0x2000000000000000, 0x3ffffffffffffffe, 0x3fffffffffffffff, 0x4000000000000000,
0x7ffffffffffffffe, 0x7fffffffffffffff, 0x8000000000000000, SIZE_MAX};
for (const auto size : refused) {
std::memset(buffer, 0xaa, sizeof(buffer));
const int error = wmemcpy_s_nid_postfix(buffer, size, source, 1);
Require(error == GuestEinval && buffer[0] != 1);
}
}

void CheckStrcpyS() {
char buffer[64];
std::memset(buffer, Poison, sizeof(buffer));
Require(strcpy_s_nid_postfix(buffer, 4, "abcdef") == GuestErange && Hex(buffer, 8) == "005a5a5a5a5a5a5a");
std::memset(buffer, Poison, sizeof(buffer));
Require(strcpy_s_nid_postfix(buffer, 0, "abc") == GuestEinval && Hex(buffer, 4) == "5a5a5a5a");
std::memset(buffer, Poison, sizeof(buffer));
Require(strcpy_s_nid_postfix(buffer, SIZE_MAX, "abc") == GuestEinval && Hex(buffer, 4) == "5a5a5a5a");
std::memset(buffer, Poison, sizeof(buffer));
Require(strcpy_s_nid_postfix(buffer, 8, nullptr) == GuestEinval && Hex(buffer, 4) == "005a5a5a");
}

void CheckStrncpyS() {
char buffer[64];
std::memset(buffer, Poison, sizeof(buffer));
Require(strncpy_s_nid_postfix(buffer, 4, "abcdef", 3) == 0 && Hex(buffer, 8) == "616263005a5a5a5a");
std::memset(buffer, Poison, sizeof(buffer));
Require(strncpy_s_nid_postfix(buffer, 4, "abcdef", 4) == GuestErange && Hex(buffer, 8) == "005a5a5a5a5a5a5a");
std::memset(buffer, Poison, sizeof(buffer));
Require(strncpy_s_nid_postfix(buffer, 4, "abcdef", SIZE_MAX) == GuestEinval && Hex(buffer, 8) == "005a5a5a5a5a5a5a");
}

void CheckFopenS() {
const std::string root = "guest_libc_annexk_rsizemax";
const std::string existing = root + "/exists.txt";
const std::string missing = root + "/missing.txt";
const std::string sub = root + "/sub";
std::filesystem::remove_all(root);
std::filesystem::create_directories(sub);
{
std::FILE* handle = std::fopen(existing.c_str(), "wb");
if (handle) std::fclose(handle);
}
FileStream* stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
*__error_nid_postfix() = 0;
Require(fopen_s_nid_postfix(&stream, missing.c_str(), "r") == GuestEnoent && stream == nullptr);
Require(*__error_nid_postfix() == GuestEnoent);
stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
Require(fopen_s_nid_postfix(&stream, existing.c_str(), "r") == 0 && stream != nullptr);
Require(fclose_nid_postfix(stream) == 0);
stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
Require(fopen_s_nid_postfix(&stream, existing.c_str(), "wx") == GuestEexist && stream == nullptr);
Require(*__error_nid_postfix() == GuestEexist);
stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
Require(fopen_s_nid_postfix(&stream, existing.c_str(), "z") == GuestEinval && stream == nullptr);
Require(*__error_nid_postfix() == GuestEinval);
stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
Require(fopen_s_nid_postfix(&stream, existing.c_str(), "") == GuestEinval && stream == nullptr);
Require(*__error_nid_postfix() == GuestEinval);
stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
Require(fopen_s_nid_postfix(&stream, sub.c_str(), "w") == GuestEisdir && stream == nullptr);
Require(*__error_nid_postfix() == GuestEisdir);
stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
Require(fopen_s_nid_postfix(&stream, nullptr, "r") == GuestEinval && stream == nullptr);
stream = reinterpret_cast<FileStream*>(0x5a5a5a5a5a5a5a5aull);
Require(fopen_s_nid_postfix(&stream, existing.c_str(), nullptr) == GuestEinval && stream == nullptr);
Require(fopen_s_nid_postfix(nullptr, existing.c_str(), "r") == GuestEinval);
std::error_code error;
std::filesystem::remove_all(root, error);
}

}

int main() {
CheckMemcpyS();
CheckWmemcpyS();
CheckStrcpyS();
CheckStrncpyS();
CheckFopenS();
return 0;
}
6 changes: 3 additions & 3 deletions core/libs/tests/GuestStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static void CheckStandardStrings() {

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;
constexpr std::size_t RsizeMax = 0x1fffffffffffffff;
const std::array<unsigned char, 8> 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}) {
Expand Down Expand Up @@ -133,7 +133,7 @@ static void CheckBoundsCheckedFunctions() {
Require(strncat_s_nid_postfix(catOverlap + 3, 10, catOverlap, 3) == 22 && catOverlap[3] == '\0');
char nonOverlap[16] = {};
std::strcpy(nonOverlap + 8, "cd");
Require(strcpy_s_nid_postfix(nonOverlap, SIZE_MAX >> 1, nonOverlap + 8) == 0 && std::strcmp(nonOverlap, "cd") == 0);
Require(strcpy_s_nid_postfix(nonOverlap, 0x1fffffffffffffff, nonOverlap + 8) == 0 && std::strcmp(nonOverlap, "cd") == 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);
Expand All @@ -149,7 +149,7 @@ static void CheckBoundsCheckedFunctions() {
}

static void CheckSecureStringSizeLimits() {
constexpr std::size_t RsizeMax = SIZE_MAX >> 1;
constexpr std::size_t RsizeMax = 0x1fffffffffffffff;
const std::array<char, 8> original = {'!', 'a', 'b', '\0', 'x', 'y', 'z', '!'};
for (const auto size : {RsizeMax + 1, SIZE_MAX}) {
for (const char* source : {"cd", "", static_cast<const char*>(nullptr)}) {
Expand Down
6 changes: 3 additions & 3 deletions core/libs/tests/GuestWideStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ int main() {
wmemset_nid_postfix(memBuf, u'Z', 16);

require(wmemcpy_s_nid_postfix(memBuf, 4, u"12345", 5) == 34 && memBuf[0] == 0 && memBuf[3] == 0);
require(wmemcpy_s_nid_postfix(memBuf, ((SIZE_MAX >> 1) / sizeof(char16_t)) + 1, u"a", 1) == 34);
require(wmemcpy_s_nid_postfix(memBuf, 0x2000000000000000, u"a", 1) == 22);
wmemcpy_s_nid_postfix(memBuf, 16, u"abcdefgh", 8);

require(wmemcpy_s_nid_postfix(memBuf + 2, 8, memBuf, 4) == 22 && memBuf[2] == 0);
Expand All @@ -144,7 +144,7 @@ int main() {
require(wmemmove_s_nid_postfix(nullptr, 16, memBuf, 3) == 22);
require(wmemmove_s_nid_postfix(memBuf, 16, nullptr, 3) == 22 && memBuf[0] == 0);
require(wmemmove_s_nid_postfix(memBuf, 4, u"12345", 5) == 34 && memBuf[0] == 0);
require(wmemmove_s_nid_postfix(memBuf, ((SIZE_MAX >> 1) / sizeof(char16_t)) + 1, u"a", 1) == 34);
require(wmemmove_s_nid_postfix(memBuf, 0x2000000000000000, u"a", 1) == 22);

char16_t strBuf[16];
require(wcsncpy_s_nid_postfix(strBuf, 16, u"hello", 5) == 0 && same(strBuf, u"hello", 6));
Expand All @@ -158,7 +158,7 @@ int main() {
require(wcsncpy_s_nid_postfix(strBuf, 4, u"hello", 5) == 34 && strBuf[0] == 0);
strBuf[0] = u'X';
require(wcsncpy_s_nid_postfix(strBuf, 5, u"hello", 5) == 34 && strBuf[0] == 0);
require(wcsncpy_s_nid_postfix(strBuf, ((SIZE_MAX >> 1) / sizeof(char16_t)) + 1, u"a", 1) == 34);
require(wcsncpy_s_nid_postfix(strBuf, 0x2000000000000000, u"a", 1) == 22);
wcsncpy_s_nid_postfix(strBuf, 16, u"abcdef", 6);

require(wcsncpy_s_nid_postfix(strBuf + 1, 10, strBuf, 3) == 22 && strBuf[1] == 0);
Expand Down
Loading
Loading