Merge pull request #4455 from SibteProf/fix/libc-secure-copy-size-limits
fix(libc): reject oversized secure copy destinations
This commit is contained in:
commit
b9f194a890
|
|
@ -311,7 +311,9 @@ 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 (!src || count > destsz) {
|
||||
std::memset(dest, 0, destsz);
|
||||
return src ? GuestErange : GuestEinval;
|
||||
|
|
@ -330,7 +332,9 @@ 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 (!src || count > destsz) {
|
||||
std::memset(dest, 0, destsz);
|
||||
return src ? GuestErange : GuestEinval;
|
||||
|
|
|
|||
|
|
@ -72,6 +72,40 @@ static bool CheckMemcpyOverlap() {
|
|||
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<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}) {
|
||||
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<const void*>(source), static_cast<const void*>(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<unsigned char, 8> 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<unsigned char, 8> 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<unsigned char, 8> 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);
|
||||
|
|
@ -344,5 +378,6 @@ int main() {
|
|||
Require(strcasestr_nid_postfix("", "a") == nullptr);
|
||||
const char highBytes[] = {static_cast<char>(0xff), 'A', 0};
|
||||
Require(strcasestr_nid_postfix(highBytes, "a") == highBytes + 1);
|
||||
CheckSecureCopySizeLimits();
|
||||
return CheckMemcpyOverlap() ? 0 : 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -337,6 +337,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceKernelAddTimerEvent](../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) - `usec` is taken as the period in microseconds; re-adding an existing timer keeps its pending expiration count and restarts the period from the time of the call, where FreeBSD 12+ `filt_timertouch` clears pending expirations on `EV_ADD`; `usec == 0` fires on every wait. None of this is confirmed on a PS5 title
|
||||
- [sceKernelWaitEqueue](../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) - a level-triggered user event (`sceKernelAddUserEvent`) is reported at most once per call, as in FreeBSD `kqueue_scan` (`sys/kern/kern_event.c`), which re-queues a reported knote without `EV_CLEAR` or `EV_DISPATCH` behind the scan marker, and kevent(2), where several triggers do not produce several kevents. Not measured on a console. Events are scanned in list order and the list is not rotated, so when `num` is smaller than the number of ready level events the later ones can starve, where FreeBSD moves a reported level knote to the tail. Several completions of one AMPR or VideoOut event within one call are returned as separate entries, where FreeBSD would aggregate them; the Orbis behaviour is unconfirmed
|
||||
- [sceRtcFormatRFC2822, sceRtcFormatRFC2822LocalTime, sceRtcFormatRFC3339LocalTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - only shadPS4 (PS4) has bodies. A null tick returns `INVALID_POINTER` like the existing `sceRtcFormatRFC3339`, where shadPS4 formats the current time instead; offsets outside ±1439 minutes return `INVALID_VALUE` like `sceRtcFormatRFC3339`; the local-time variants use the host's offset at the given instant (shadPS4 uses `sceKernelGettimezone`, the current offset)
|
||||
- [memcpy_s and memmove_s](../../core/libs/prx/libc/src/Strings.cpp) (libc) use the C11 K.3.7.1.1 and K.3.7.1.2 size constraints, with RSIZE_MAX taken as SIZE_MAX >> 1 as in memset_s. An oversized destination size returns ERANGE (34) without touching the destination; the console's RSIZE_MAX and exact error value are unverified. Constraint violations return an error without invoking the registered constraint handler, as before.
|
||||
- [snwprintf_s](../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - follows C11 K.3.9.1.3 with `RSIZE_MAX` assumed to be `SIZE_MAX >> 1`. A runtime-constraint violation only returns a negative value: `set_constraint_handler_s` is not exported and the console's default handler is unknown. An invalid multibyte `%s` argument is copied byte by byte instead of being reported as an encoding error, as in `vswprintf`
|
||||
- [vsnprintf_s and vsscanf_s](../../core/libs/prx/libc/src/Formatting.cpp) (libc) - `vsnprintf_s` forwards to `vsnprintf` semantics: the C11 K.3.7.1.5 runtime constraints (null buffer, `size == 0`, `%n`, null `%s`) are not checked, so `size == 0` returns the would-be length instead of a negative value, and no constraint handler is invoked. `vsscanf_s` returns the count of items converted before an undersized `%s`/`%c`/`%[` destination (0 when it is the first), not EOF. Neither is confirmed on a PS5 title
|
||||
- [libSceUlt ulthreads](../../core/libs/prx/libSceUlt/Export.cpp) - `_sceUltUlthreadRuntimeOptParamInitialize`, `_sceUltUlthreadRuntimeCreate` and `_sceUltUlthreadCreate` are assumed to take the same arguments as the existing exports without the underscore, including the trailing build version. `sceUltUlthreadTryJoin` is assumed to return `ULT_ERROR_BUSY` while the ulthread runs, and `sceUltUlthreadRuntimeDestroy` to return `ULT_ERROR_BUSY` while an ulthread created on the runtime is not joined. Neither error has been confirmed on PS5 hardware
|
||||
|
|
|
|||
Loading…
Reference in New Issue