Merge pull request #3046 from MarcZX100/codex/libc-secure-format-scan-3032

feat(libc): implement vsnprintf_s and vsscanf_s
This commit is contained in:
Adrià 2026-10-11 00:05:28 +02:00 committed by GitHub
commit 0de1dfdff8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 52 additions and 13 deletions

View File

@ -25,19 +25,6 @@ int APS5_VABI _ZSt14_Atomic_assertPKcS0__nid_postfix() {
return 0;
}
// Live Cyberpunk 2077 import used in an fopen/fseek/ftell-like file-size
// idiom as (handle, 0, 2); returning 0 reports success.
int APS5_VABI vsnprintf_s_nid_postfix() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
// Live Cyberpunk 2077 import used as (handle, 0, 0) in the same file-size
// idiom; returning 0 reports success.
int APS5_VABI vsscanf_s_nid_postfix() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
APS5_EXPORT("Ye20uNnlglA", libcCyberUnknown02);

View File

@ -408,6 +408,15 @@ int APS5_VABI vsnprintf_nid_postfix(char* str, size_t size, const char* format,
#ifdef _WIN32
int APS5_VABI vsscanf_s_nid_postfix(const char* buffer, const char* format, VaList* args) {
LibcDetail::FormatArguments arguments(args);
return ScanGuest(buffer, format, true, [&] { return arguments.Next<void*>(); }, [&] { return arguments.Next<unsigned int>(); });
}
int APS5_VABI vsnprintf_s_nid_postfix(char* buffer, size_t size, const char* format, VaList* args) {
return LibcDetail::FormatWindows(buffer, size, format, args);
}
int APS5_VABI snprintf_s_nid_postfix(char* buffer, size_t size, const char* format, ...) {
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, format);
@ -426,6 +435,20 @@ int APS5_VABI printf_s_nid_postfix(const char* format, ...) {
#else
int APS5_VABI vsscanf_s_nid_postfix(const char* buffer, const char* format, VaList* args) {
LibcDetail::FormatArguments arguments(args);
return ScanGuest(buffer, format, true, [&] { return arguments.Next<void*>(); }, [&] { return arguments.Next<unsigned int>(); });
}
int APS5_VABI vsnprintf_s_nid_postfix(char* buffer, size_t size, const char* format, VaList* args) {
if (HasGuestWideFormat(format)) return LibcDetail::FormatWindows(buffer, size, format, args);
std::va_list copy;
va_copy(copy, *reinterpret_cast<std::va_list*>(args));
const int result = std::vsnprintf(buffer, size, format, copy);
va_end(copy);
return result;
}
int APS5_VABI vsprintf_s_nid_postfix(char* buffer, size_t size, const char* format, VaList* args) {
if (HasGuestWideFormat(format)) return LibcDetail::FormatWindows(buffer, size, format, args);
std::va_list copy;

View File

@ -11,6 +11,8 @@ int APS5_VABI printf_nid_postfix(const char*, ...);
int APS5_VABI libc_printf_nid_postfix(const char*, ...);
int APS5_VABI sscanf_nid_postfix(const char*, const char*, ...);
int APS5_VABI vsnprintf_nid_postfix(char*, size_t, const char*, VaList*);
int APS5_VABI vsnprintf_s_nid_postfix(char*, size_t, const char*, VaList*);
int APS5_VABI vsscanf_s_nid_postfix(const char*, const char*, VaList*);
int APS5_VABI vprintf_nid_postfix(const char*, VaList*);
}
@ -30,6 +32,26 @@ static int APS5_VABI FormatList(char* buffer, size_t size, const char* format, .
return result;
}
static int APS5_VABI FormatListS(char* buffer, size_t size, const char* format, ...) {
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, format);
VaList list;
std::memcpy(&list, args, sizeof(list));
const int result = vsnprintf_s_nid_postfix(buffer, size, format, &list);
__builtin_sysv_va_end(args);
return result;
}
static int APS5_VABI ScanListS(const char* buffer, const char* format, ...) {
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, format);
VaList list;
std::memcpy(&list, args, sizeof(list));
const int result = vsscanf_s_nid_postfix(buffer, format, &list);
__builtin_sysv_va_end(args);
return result;
}
static int APS5_VABI PrintList(const char* format, ...) {
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, format);
@ -137,6 +159,12 @@ __attribute__((noinline)) static void APS5_VABI RunChecks() {
Require(libc_printf_nid_postfix("libc_printf: %s\n", "OK") == 16);
Require(libc_printf_nid_postfix("%d %d %d %d %d %d %d\n", 1, 22, 333, 4444, 55555, 666666, 7777777) == 35);
Require(PrintList("vprintf: %d\n", 42) == 12);
Require(FormatListS(buffer, sizeof(buffer), "%d-%s", 42, "x") == 4 && std::strcmp(buffer, "42-x") == 0);
int scanNum = 0;
char scanWord[8] = {};
Require(ScanListS("123 test", "%d %s", &scanNum, scanWord, 8u) == 2 && scanNum == 123 && std::strcmp(scanWord, "test") == 0);
char tooSmall[4] = {'x', 'x', 'x', 'x'};
Require(ScanListS("abcdef", "%s", tooSmall, 4u) == 0 && tooSmall[0] == '\0');
std::puts("Formatting checks passed: 10000 iterations");
}

View File

@ -253,6 +253,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
- [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)
- [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
- [libSceUlt queues](../../core/libs/prx/libSceUlt/Export.cpp) - the added `sceUltQueuePop`, `sceUltQueueTryPush`, `sceUltQueueDestroy` and `sceUltQueueDataResourcePoolDestroy` signatures are inferred from the existing push/pop and object APIs. Queue destruction is assumed to reject blocked callers with `ULT_ERROR_BUSY` and otherwise discard queued data; pool exhaustion returns `ULT_ERROR_AGAIN`. These rules, alignment checks and shared slot limits have not been confirmed on PS5 hardware or a title. Non-null queue and data-pool option parameters throw because their layouts are unknown.
- [AC1FtjqMCL0](../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - unknown name, signature