Merge pull request #13798 from pushkarreddyy/fix/app-info-efault

fix(libkernel): return EFAULT from sceKernelGetAppInfo for invalid memory pointers
This commit is contained in:
Adrià authored and GitHub committed 2026-11-01 02:22:03 +01:00
commit cd35d6103a
3 files changed
+16 -6

No files matched your search

@@ -662,18 +662,21 @@ int APS5_VABI sceKernelGetAppInfo(int pid, void* info) {
constexpr std::size_t TitleIdOffset = 0x10;
constexpr std::size_t TitleIdBytes = 10;
constexpr std::size_t MarkerOffset = 0x50;
auto* bytes = static_cast<std::uint8_t*>(info);
std::memset(bytes, 0, StructureBytes);
std::uint8_t record[StructureBytes]{};
if (own) {
const auto titleId = GetAppTitleId_nid_postfix();
char field[TitleIdBytes] = {};
std::strncpy(field, titleId.value, sizeof(field) - 1);
const std::uint32_t appId = 1;
std::memcpy(bytes, &appId, sizeof(appId));
std::memcpy(bytes + TitleIdOffset, field, TitleIdBytes);
std::memcpy(record, &appId, sizeof(appId));
std::memcpy(record + TitleIdOffset, field, TitleIdBytes);
}
const std::uint32_t marker = 0xffffffffu;
std::memcpy(bytes + MarkerOffset, &marker, sizeof(marker));
std::memcpy(record + MarkerOffset, &marker, sizeof(marker));
const int savedErrno = errno;
const bool written = WriteGuest(info, record, sizeof(record));
errno = savedErrno;
if (!written) return SCE_KERNEL_ERROR_EFAULT;
return 0;
}
+7
View File
@@ -1,6 +1,7 @@
#include "prx/libkernel/KernelErrors.hpp"
#include "prx/libkernel/include/Api.hpp"
#include "SceTypes.hpp"
#include <cerrno>
#include <cstdint>
#include <cstdlib>
#include <cstring>
@@ -98,6 +99,12 @@ int main(int argc, char** argv) {
std::memset(expected + 0x50, 0xff, 4);
Require(std::memcmp(wide, expected, sizeof(wide)) == 0);
Require(sceKernelGetAppInfo(self, nullptr) == 0);
errno = 71;
Require(sceKernelGetAppInfo(self, reinterpret_cast<void*>(0x10)) == SCE_KERNEL_ERROR_EFAULT);
Require(errno == 71);
errno = 71;
Require(sceKernelGetAppInfo(0, reinterpret_cast<void*>(0x10)) == SCE_KERNEL_ERROR_EFAULT);
Require(errno == 71);
std::filesystem::current_path(previous);
std::filesystem::remove_all(root);
const auto empty = std::filesystem::temp_directory_path() / ("guest_app_info_empty_" + std::to_string(self));
+1 -1
View File
@@ -45,7 +45,7 @@
- [sceKernelGettimezone](../../../core/libs/prx/libkernel/Time/Time.cpp) (libkernel) reports the host zone, not the console's time zone setting: `tz_minuteswest` holds the standard offset in minutes east of UTC, despite its name, and `tz_dsttime` the zone's daylight saving amount in minutes, found as for the last argument of `sceKernelConvertLocaltimeToUtc`. A PS5 on firmware 13.40 set to US Eastern returned -300 and 60 (measured 2026-10-19, in summer time); what it returns outside summer time or for a zone without daylight saving was not measured. The Windows limits of the entry above apply.
- [getuid, geteuid, getgid, getegid, getresuid, getresgid and issetugid](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) report the root identity (0) and a process that never changed credentials (`issetugid` 0), as a jailbroken process has; the host user and group are not exposed to the guest. `getresuid` and `getresgid` follow FreeBSD 11.4's `sys_getresuid` and `sys_getresgid`, writing 0 to non-null pointers and leaving errno untouched on success. The values a title sees on a console are unverified
- [pthread_condattr_getclock](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) - a null output pointer with a valid attribute throws; FreeBSD faults there and the console behavior is unverified. Null attribute pointers and null handles fail with EINVAL as in FreeBSD, not measured on a PS5. The signature follows the public PS5 payload SDK's [pthread.h](https://github.com/ps5-payload-dev/sdk/blob/f5d40f02770e9b0001dd9e18056eca70b3da8b40/include/freebsd/pthread.h). Valid initialized attributes report the stored clock ID, as in [FreeBSD 11.4 libthr](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libthr/thread/thr_condattr.c); this has not been measured on a PS5. Inaccessible non-null pointers and stale handles are not validated.
- [sceKernelGetAppInfo](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - a pid other than the caller's and 0 throws, because AnyPS5 cannot know which other processes exist: a payload process on a PS5 (kernel firmware 12.70, system software reported as 13.09) got 0 and a record for pid 1 and 0x80020003 (`ESRCH`, errno 3) for its own pid + 1, so which other pids return a record cannot be modelled. `app_id` (offset 0) is 1, the running title's id in libSceSystemService, which is a guess (0 there and at 0x10). Byte 0x1e was 5 there and is unknown in a game, so it is written as 0, as is every other field except 0xffffffff at 0x50. The 0x58-byte extent was measured only in that payload process (SDK v0.44); a title with a smaller structure could be overrun if it depends on the SDK version. Pid 0 gets the zeroed record without a title ID, as that process read it; whose record that is was not identified and a game's pid 0 record was not measured
- [sceKernelGetAppInfo](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - a pid other than the caller's and 0 throws, because AnyPS5 cannot know which other processes exist: a payload process on a PS5 (kernel firmware 12.70, system software reported as 13.09) got 0 and a record for pid 1 and 0x80020003 (`ESRCH`, errno 3) for its own pid + 1, so which other pids return a record cannot be modelled. `app_id` (offset 0) is 1, the running title's id in libSceSystemService, which is a guess (0 there and at 0x10). Byte 0x1e was 5 there and is unknown in a game, so it is written as 0, as is every other field except 0xffffffff at 0x50. The 0x58-byte extent was measured only in that payload process (SDK v0.44); a title with a smaller structure could be overrun if it depends on the SDK version. Pid 0 gets the zeroed record without a title ID, as that process read it; whose record that is was not identified and a game's pid 0 record was not measured. An unwritable destination returns `SCE_KERNEL_ERROR_EFAULT` (0x8002000e) as a host-safety fallback: the console's failing pid + 1 row came back with errno 3 set next to the encoded return, here errno is left as the caller had it, and what the console leaves for an unwritable pointer is not measured.
- [AMPR object layout and compact gathers](../../../core/libs/prx/libkernel/Apr/include/AprCommandBuffer.hpp) - the 24-byte object and offsets (type 0, cursor 4, count 8, capacity 12, pointer 16) follow [ampr_emu assertions](https://github.com/drakmor/ampr_emu/blob/cfa85df379f6eeeb165d7badf9b648e266fe77b7/include/ampr_emu_command_buffer_types.h#L138-L143) and its [C declaration](https://github.com/drakmor/ampr_emu/blob/cfa85df379f6eeeb165d7badf9b648e266fe77b7/include/ampr.h#L41-L47). AnyPS5 represents the 32-bit type word as low 16-bit type and high 16-bit recording flags. Command encoding is AnyPS5 internal: opcode 29 and three 32-bit words encode the high offset byte, length minus one and low offset word. The 12-byte size reduces HFW's 1 MiB buffer pressure; it is not a claim about console opcode encoding. Existing 40-bit offsets and lengths 1 through 2^32 are retained; console behavior outside those limits is unverified.
- [APR resolve ForEach result](../../../core/libs/prx/libkernel/Apr/src/Apr.cpp) (libkernel) - `sceKernelAprResolveFilepathsToIdsAndFileSizesForEach` returns the number of paths that resolved, with the per-path status in `results` and the failed ids/sizes set to the invalid id and 0. The [ampr_emu](https://github.com/drakmor/ampr_emu) bridge passes the count of its resolver through and does not show what is counted; counting successes is not confirmed on a title. The guest errno of the failing non-`ForEach` resolves is set through `__error_nid_postfix`
- [APR resolve argument checks and prefix join](../../../core/libs/prx/libkernel/Apr/src/Apr.cpp) (libkernel) - the batched `sceKernelAprResolveFilepathsToIds` forms reject a null `prefix`, `paths` or `ids` with `EINVAL`, accept a count of 0 as a success that writes nothing, take a null path element as a missing file at its own index, write the invalid id into the failing slot, and leave `error_index` untouched on success; the prefixed forms join prefix and path by plain concatenation. None of these failure and validation details is measured on a title, and all of them differ from the [ampr_emu](https://github.com/drakmor/ampr_emu) bridge, which answers `EFAULT` for null arguments and rejects a count of 0, validates every path element before resolving any, leaves the failing slot unwritten, sets `error_index` to 0 on success, and inserts a `/` between prefix and path unless the prefix already ends in a separator