From e67d50cb2fc6d9485a65e542422218b812e9759d Mon Sep 17 00:00:00 2001 From: Pouare514 Date: Mon, 5 Oct 2026 01:10:25 +0200 Subject: [PATCH] fix(libs): route DateTime zone suffix through RFC3339 path, throw on unverified shapes --- core/libs/prx/libSceAudioIn/Export.cpp | 2 +- core/libs/prx/libSceRtc/CMakeLists.txt | 3 +- core/libs/prx/libSceRtc/Export.cpp | 55 ++++++++++++++++---------- core/libs/tests/GuestRtc.cpp | 26 ++++++++++++ docs/dev/TechnicalDebt.md | 2 +- 5 files changed, 64 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceAudioIn/Export.cpp b/core/libs/prx/libSceAudioIn/Export.cpp index 3c31aaa40..cdd232455 100644 --- a/core/libs/prx/libSceAudioIn/Export.cpp +++ b/core/libs/prx/libSceAudioIn/Export.cpp @@ -122,7 +122,7 @@ int APS5_VABI sceAudioInInput(int handle, void* dest) { if (device != 0) { captured = capture(device, out, bytes, deadline); } else { - std::this_thread::sleep_until(wake); + while (Clock::now() < wake) std::this_thread::sleep_until(wake); } std::memset(out + captured, 0, bytes - captured); lock.lock(); diff --git a/core/libs/prx/libSceRtc/CMakeLists.txt b/core/libs/prx/libSceRtc/CMakeLists.txt index a0e4a2444..faf3100ac 100644 --- a/core/libs/prx/libSceRtc/CMakeLists.txt +++ b/core/libs/prx/libSceRtc/CMakeLists.txt @@ -11,7 +11,8 @@ endif() if(BUILD_TESTING) add_test_executable(guest_rtc_tests ${LIBS_INCLUDE_DIR}/tests/GuestRtc.cpp) target_include_directories(guest_rtc_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_rtc_tests PRIVATE libSceRtc) + target_link_libraries(guest_rtc_tests PRIVATE libSceRtc libc) + configure_windows_unwind(guest_rtc_tests) add_test(NAME guest_rtc COMMAND guest_rtc_tests) if(WIN32) set_tests_properties(guest_rtc PROPERTIES ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceRtc/Export.cpp b/core/libs/prx/libSceRtc/Export.cpp index 8ec554311..272b78cb3 100644 --- a/core/libs/prx/libSceRtc/Export.cpp +++ b/core/libs/prx/libSceRtc/Export.cpp @@ -139,6 +139,23 @@ bool parseDigits(const char*& cursor, int count, int& value) { return true; } +bool parseZoneSuffix(const char*& cursor, std::int64_t& offsetMinutes) { + offsetMinutes = 0; + if (*cursor == 'Z' || *cursor == 'z') { + ++cursor; + return true; + } + if (*cursor == '+' || *cursor == '-') { + const int sign = *cursor++ == '-' ? -1 : 1; + int offsetHours = 0, offsetMinute = 0; + if (!parseDigits(cursor, 2, offsetHours) || *cursor++ != ':' || !parseDigits(cursor, 2, offsetMinute)) return false; + if (offsetHours > 23 || offsetMinute > 59) return false; + offsetMinutes = sign * (offsetHours * 60 + offsetMinute); + return true; + } + return false; +} + } extern "C" { @@ -282,17 +299,7 @@ int APS5_VABI sceRtcParseRFC3339(RtcTick* utc, const char* date_time) { for (; *cursor >= '0' && *cursor <= '9'; ++cursor, scale /= 10) microsecond += static_cast(*cursor - '0') * scale; } std::int64_t offsetMinutes = 0; - if (*cursor == 'Z' || *cursor == 'z') { - ++cursor; - } else if (*cursor == '+' || *cursor == '-') { - const int sign = *cursor++ == '-' ? -1 : 1; - int offsetHours = 0, offsetMinute = 0; - if (!parseDigits(cursor, 2, offsetHours) || *cursor++ != ':' || !parseDigits(cursor, 2, offsetMinute)) return SCE_RTC_ERROR_BAD_PARSE; - if (offsetHours > 23 || offsetMinute > 59) return SCE_RTC_ERROR_BAD_PARSE; - offsetMinutes = sign * (offsetHours * 60 + offsetMinute); - } else { - return SCE_RTC_ERROR_BAD_PARSE; - } + if (!parseZoneSuffix(cursor, offsetMinutes)) return SCE_RTC_ERROR_BAD_PARSE; if (*cursor != 0) return SCE_RTC_ERROR_BAD_PARSE; const RtcDateTime time{static_cast(year), static_cast(month), static_cast(day), static_cast(hour), static_cast(minute), static_cast(second), microsecond}; @@ -305,23 +312,29 @@ int APS5_VABI sceRtcParseDateTime(RtcTick* utc, const char* date_time) { if (!utc || !date_time) return SCE_RTC_ERROR_INVALID_POINTER; const char* cursor = date_time; int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; - if (!parseDigits(cursor, 4, year) || *cursor++ != '-' || !parseDigits(cursor, 2, month) || *cursor++ != '-' || !parseDigits(cursor, 2, day)) return SCE_RTC_ERROR_BAD_PARSE; - if (*cursor != 'T' && *cursor != 't' && *cursor != ' ') return SCE_RTC_ERROR_BAD_PARSE; - ++cursor; - if (!parseDigits(cursor, 2, hour) || *cursor++ != ':' || !parseDigits(cursor, 2, minute) || *cursor++ != ':' || !parseDigits(cursor, 2, second)) return SCE_RTC_ERROR_BAD_PARSE; + bool ok = parseDigits(cursor, 4, year) && *cursor++ == '-' && parseDigits(cursor, 2, month) && *cursor++ == '-' && parseDigits(cursor, 2, day); + ok = ok && (*cursor == 'T' || *cursor == 't' || *cursor == ' '); + if (ok) ++cursor; + ok = ok && parseDigits(cursor, 2, hour) && *cursor++ == ':' && parseDigits(cursor, 2, minute) && *cursor++ == ':' && parseDigits(cursor, 2, second); std::uint32_t microsecond = 0; - if (*cursor == '.') { + if (ok && *cursor == '.') { ++cursor; std::uint32_t scale = 100000; - if (*cursor < '0' || *cursor > '9') return SCE_RTC_ERROR_BAD_PARSE; - for (; *cursor >= '0' && *cursor <= '9'; ++cursor, scale /= 10) microsecond += static_cast(*cursor - '0') * scale; + ok = *cursor >= '0' && *cursor <= '9'; + for (; ok && *cursor >= '0' && *cursor <= '9'; ++cursor, scale /= 10) microsecond += static_cast(*cursor - '0') * scale; + } + std::int64_t offsetMinutes = 0; + if (ok && *cursor != 0) ok = parseZoneSuffix(cursor, offsetMinutes); + if (ok && *cursor != 0) ok = false; + if (!ok) { + NotImplemented_nid_no_patch(__func__); + return SCE_RTC_ERROR_BAD_PARSE; } - if (*cursor != 0) return SCE_RTC_ERROR_BAD_PARSE; const RtcDateTime time{static_cast(year), static_cast(month), static_cast(day), static_cast(hour), static_cast(minute), static_cast(second), microsecond}; if (const int result = validate(&time); result != 0) return result; - utc->tick = toTick(time); - return 0; + const RtcTick local{toTick(time)}; + return addTicks(utc, &local, -offsetMinutes, TICKS_PER_MINUTE); } int APS5_VABI sceRtcTickAddTicks(RtcTick* dst, const RtcTick* src, int64_t ticks) { diff --git a/core/libs/tests/GuestRtc.cpp b/core/libs/tests/GuestRtc.cpp index 8016c239b..23d203f06 100644 --- a/core/libs/tests/GuestRtc.cpp +++ b/core/libs/tests/GuestRtc.cpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { int APS5_VABI sceRtcCheckValid(const RtcDateTime*); @@ -21,6 +22,7 @@ int APS5_VABI sceRtcGetWin32FileTime(const RtcDateTime*, std::uint64_t*); int APS5_VABI sceRtcSetWin32FileTime(RtcDateTime*, std::uint64_t); int APS5_VABI sceRtcFormatRFC3339(char*, const RtcTick*, int); int APS5_VABI sceRtcParseRFC3339(RtcTick*, const char*); +int APS5_VABI sceRtcParseDateTime(RtcTick*, const char*); int APS5_VABI sceRtcTickAddTicks(RtcTick*, const RtcTick*, std::int64_t); int APS5_VABI sceRtcTickAddSeconds(RtcTick*, const RtcTick*, std::int64_t); int APS5_VABI sceRtcTickAddDays(RtcTick*, const RtcTick*, std::int32_t); @@ -123,6 +125,30 @@ int main() { Require(sceRtcParseRFC3339(&tick, "2024-02-29T12:34:56.789-23:59") == 0 && tick.tick == leapDayTick + 86340000000ull); Require(sceRtcParseRFC3339(nullptr, "1970-01-01T00:00:00Z") == invalidPointer); + Require(sceRtcParseDateTime(&tick, "2024-02-29T12:34:56.789") == 0 && tick.tick == leapDayTick); + Require(sceRtcParseDateTime(&tick, "2024-02-29 12:34:56") == 0 && tick.tick == leapDayTick - 789000ull); + Require(sceRtcParseDateTime(&tick, "2024-02-29T14:04:56.789+01:30") == 0 && tick.tick == leapDayTick); + Require(sceRtcParseDateTime(&tick, "2024-02-29t12:34:56.789z") == 0 && tick.tick == leapDayTick); + Require(sceRtcParseDateTime(&tick, "1970-01-01T00:00:00Z") == 0 && tick.tick == unixEpochTick); + Require(sceRtcParseDateTime(&tick, "2023-02-29T00:00:00") == invalidDay); + Require(sceRtcParseDateTime(nullptr, "1970-01-01T00:00:00Z") == invalidPointer); + const char* unparseable[] = { + "2024-02-29", + "2024/02/29T12:34:56", + "2024-02-29T12:34:56Zjunk", + "2024-02-29T12:34:56+99:99", + "2024-02-29T12:34:56.", + }; + for (const char* text : unparseable) { + bool thrown = false; + try { + sceRtcParseDateTime(&tick, text); + } catch (const std::exception&) { + thrown = true; + } + Require(thrown); + } + RtcTick source{leapDayTick}; RtcTick result{}; Require(sceRtcTickAddSeconds(&result, &source, 3600) == 0 && result.tick == leapDayTick + 3600000000ull); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 22d1e657d..b7b3a93f3 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -23,7 +23,7 @@ Throughout the project, every function at every stage either **does exactly what ### Unknown function info - [sceVideoOutOpen](../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - the open param's first word is unknown (PPSA21564 passes 16; it is not the byte size, since the affinity mask is at offset 16); only 16 is accepted. Whether the param continues past offset 24 is also unknown -- [sceRtcParseDateTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - accepted format assumed to be the ISO 8601 subset `YYYY-MM-DDTHH:MM:SS[.ffffff]` (T/t/space separator, optional fractional seconds), matching the sibling RFC 3339 parser; unverified against hardware +- [sceRtcParseDateTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - accepted format assumed to be the ISO 8601 subset `YYYY-MM-DDTHH:MM:SS[.ffffff]` (T/t/space separator, optional fractional seconds) with the sibling RFC 3339 zone suffix (`Z` or `±HH:MM`); a string without a zone is taken as UTC, and anything outside this shape throws instead of returning a parse error, unverified against hardware - [AMPR WaitOnAddress / WaitOnCounter](../../core/libs/prx/libkernel/Apr/src/Apr.cpp) (libSceAmpr) - compare encoding assumed to be the WAIT_REG_MEM one (0 always, 1 <, 2 <=, 3 ==, 4 !=, 5 >=, 6 >) - [sceAgcSetSubmitMode](../../core/libs/prx/libSceAgc/Misc/src/Suspend.cpp) (libSceAgc) - mode values unknown; only 0 is accepted - [zARR5aCmkoY](../../core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp) (libSceAgc) - unknown name, signature