Merge pull request #10830 from babyhuey/feat/libkernel-kern-proc-pid

feat(libkernel): answer kern.proc.pid for the calling process
This commit is contained in:
Dean authored and GitHub committed 2026-10-27 10:54:49 +13:00
commit 3c4aab5acc
3 files changed
+77

No files matched your search

@@ -155,6 +155,20 @@ struct GuestVmEntry {
};
static_assert(offsetof(GuestVmEntry, start) == 8 && offsetof(GuestVmEntry, protection) == 56 && offsetof(GuestVmEntry, path) == 136 && sizeof(GuestVmEntry) == 144);
struct GuestKinfoProc {
std::int32_t structSize;
std::int32_t layout;
unsigned char reserved0[64];
std::int32_t pid;
std::int32_t parentPid;
unsigned char reserved1[288];
std::int64_t flag;
unsigned char reserved2[71];
char command[20];
unsigned char reserved3[621];
};
static_assert(offsetof(GuestKinfoProc, pid) == 72 && offsetof(GuestKinfoProc, parentPid) == 76 && offsetof(GuestKinfoProc, flag) == 368 && offsetof(GuestKinfoProc, command) == 447 && sizeof(GuestKinfoProc) == 1088);
std::vector<GuestVmEntry> GuestVmEntries() {
std::vector<GuestVmEntry> entries;
std::uintptr_t start = 0;
@@ -360,6 +374,26 @@ int APS5_VABI sysctl_nid_postfix(const int* name, std::uint32_t nameLength, void
static constexpr char path[] = "/app0/eboot.bin";
return CopyOut(path, sizeof(path), oldValue, oldLength);
}
if (nameLength >= 3 && name[0] == 1 && name[1] == 14 && name[2] == 1) {
if (newValue != nullptr) {
*__error_nid_postfix() = 1;
return -1;
}
if (nameLength != 4) {
*__error_nid_postfix() = 22;
return -1;
}
if (name[3] != getpid_nid_postfix()) {
*__error_nid_postfix() = 3;
return -1;
}
GuestKinfoProc process{};
process.structSize = sizeof(process);
process.pid = name[3];
process.parentPid = getppid_nid_postfix();
std::memcpy(process.command, "eboot.bin", sizeof("eboot.bin"));
return CopyOut(&process, sizeof(process), oldValue, oldLength);
}
if (nameLength >= 3 && name[0] == 1 && name[1] == 14 && name[2] == 35) {
if (newValue != nullptr) {
*__error_nid_postfix() = 1;
@@ -306,6 +306,47 @@ static void CheckPathnameSysctl() {
length = sizeof(buffer);
Require(sysctl_nid_postfix(mibSelf, 4, buffer, &length, buffer, sizeof(buffer)) == -1 && *__error_nid_postfix() == 1);
}
static void CheckProcessSysctl() {
const int callerPid = getpid_nid_postfix();
const int mibPid[] = {1, 14, 1, callerPid};
std::size_t length = 0;
Require(sysctl_nid_postfix(mibPid, 4, nullptr, &length, nullptr, 0) == 0 && length == 1088);
unsigned char buffer[1100];
std::memset(buffer, 0xaa, sizeof(buffer));
length = sizeof(buffer);
Require(sysctl_nid_postfix(mibPid, 4, buffer, &length, nullptr, 0) == 0 && length == 1088);
std::int32_t structSize = 0;
std::int32_t pid = 0;
std::int32_t parentPid = 0;
std::int64_t flag = -1;
std::memcpy(&structSize, buffer, sizeof(structSize));
std::memcpy(&pid, buffer + 72, sizeof(pid));
std::memcpy(&parentPid, buffer + 76, sizeof(parentPid));
std::memcpy(&flag, buffer + 368, sizeof(flag));
Require(structSize == 1088 && pid == callerPid && parentPid == getppid_nid_postfix());
Require((flag & 0x800) == 0);
Require(std::strcmp(reinterpret_cast<const char*>(buffer + 447), "eboot.bin") == 0);
Require(buffer[4] == 0 && buffer[80] == 0 && buffer[1087] == 0 && buffer[1088] == 0xaa);
const int mibForeign[] = {1, 14, 1, callerPid + 1000};
Require(sysctl_nid_postfix(mibForeign, 4, buffer, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 3);
const int mibSelfAlias[] = {1, 14, 1, -1};
Require(sysctl_nid_postfix(mibSelfAlias, 4, buffer, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 3);
const int mibTooShort[] = {1, 14, 1};
Require(sysctl_nid_postfix(mibTooShort, 3, buffer, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 22);
const int mibTooLong[] = {1, 14, 1, callerPid, 0};
Require(sysctl_nid_postfix(mibTooLong, 5, buffer, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 22);
length = sizeof(buffer);
Require(sysctl_nid_postfix(mibPid, 4, buffer, &length, buffer, sizeof(buffer)) == -1 && *__error_nid_postfix() == 1);
length = 100;
Require(sysctl_nid_postfix(mibPid, 4, buffer, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 12 && length == 100);
}
struct SystemString {
int mib[2];
const char* name;
@@ -982,6 +1023,7 @@ int main() {
CheckUnansweredSysctl();
CheckUnknownSysctlName();
CheckPathnameSysctl();
CheckProcessSysctl();
CheckSystemStringSysctl();
CheckSystemStringNames();
CheckOsReleaseDateSysctl();
+1
View File
@@ -208,6 +208,7 @@
- [execv, execvp and execve](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) replace the running image with the converted guest executable, following FreeBSD 11.4 `exec_copyin_args`: a null path is `EBADF` (measured), a null `argv` `EFAULT`, an empty path `ENOENT`; an empty `argv` and a null or unpaired `envp` are copied, `E2BIG` on their bytes; a lookup misses with `ENOENT`, a path over a regular file with `ENOTDIR`, and a regular file with no execute permission `EACCES` (0644 measured); `execvp` walks `PATH` skipping non-regular and inexecutable candidates, keeping their `EACCES`, or ends with `ENOENT`. Linux replaces through `fexecve`, returning `ENOEXEC` (measured) for a non-x86-64 target and `EACCES` for one without execute permission; Windows probes the PE header for the same `ENOEXEC`. A missing converted artifact throws. Windows execute permission is libc `access`'s ACL. The replacement keeps the host `environ`, `execve`'s `envp` name overrides
- [sysarch](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - `AMD64_GET_FSBASE`, `AMD64_GET_GSBASE` and `AMD64_SET_GSBASE` follow FreeBSD 11's amd64 [`sysarch`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/amd64/amd64/sys_machdep.c) on Linux x86-64 hosts, through the host's `arch_prctl`. The GS base belongs to the calling thread, a base at or above `0x800000000000` (`VM_MAXUSER_ADDRESS`) returns `EINVAL`, and a null argument returns `EFAULT`. `AMD64_SET_FSBASE` throws, since guest and host code share the thread's FS base for their TLS. In a payload process on a PS5 (firmware 12.70), `I386_GET_FSBASE` (7) and `I386_GET_GSBASE` (9) wrote 4 bytes, the low 32 bits of the FS and GS bases, and failed with `EFAULT` for a null argument; here they do the same on Linux x86-64 hosts. There a GS base set by one thread left the main thread's unchanged, and threads made with `pthread_create` and `scePthreadCreate` started with their creator's GS base; here a guest thread's host thread is created by the creating thread, and Linux's [`copy_thread`](https://github.com/torvalds/linux/blob/v6.6/arch/x86/kernel/process.c#L182-L186) gives a new thread its creator's GS base. The operation numbers FreeBSD 11's amd64 `sysarch` has no case for returned `EINVAL` there for the nine tried, each with a null argument (2, 5, 6, 12, 127, 133, 134, 1000 and -1; 6 is `I386_VM86`, which its header names), as its [default case](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/amd64/amd64/sys_machdep.c#L324-L325) does; here every such number returns `EINVAL` on every host; the numbers other than those nine follow that default case and were not measured. `AMD64_GET_XFPUSTATE` (132) returned 0 there for lengths 0, 64, 128, 256 and 512 (512 was also the largest length a search accepted) and failed with `EINVAL` for 1024, 2048, 4096 and 8192 and with `EFAULT` for a null argument or address, and `I386_GET_XFPUSTATE` (11) failed with `EFAULT` for a null argument; both throw here. The other operations FreeBSD 11's amd64 `sysarch` handles, and every operation it handles on other hosts (Windows keeps its TEB in GS), throw.
- [kern.proc.pathname](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) returns `/app0/eboot.bin` for pid -1 and the calling process, following FreeBSD's `sysctl_kern_proc_pathname`; the exact pathname on retail consoles remains unverified.
- [kern.proc.pid](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - what the console fills in a process's `kinfo_proc` is unverified; `sysctl` {`CTL_KERN`, `KERN_PROC`, `KERN_PROC_PID`, pid} for the calling process returns a 1,088-byte FreeBSD 11 amd64 `kinfo_proc` ([`sys/sys/user.h`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/sys/user.h)) holding only `ki_structsize`, `ki_pid`, `ki_ppid` and `ki_comm` (`eboot.bin`, the base name of the `kern.proc.pathname` value), with every other field 0, including `ki_flag`, so `P_TRACED` (0x800) is clear. Source Port (PPSA20040) asks for its own record with a 0x440-byte buffer and tests only bit 0x800 of `ki_flag` at offset 0x170, to see whether a debugger is attached. Any other pid, including -1, fails with `ESRCH`, a write with `EPERM` and a short buffer with `ENOMEM`, as for `kern.proc.vmmap`.
- [kern.proc.env](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - which variables, if any, the console starts an application with is unverified; `sysctl` {`CTL_KERN`, `KERN_PROC`, `KERN_PROC_ENV`, pid} returns 0 bytes, the empty start-up environment `environ` and libc's `getenv` also begin with, as FreeBSD 11's [`sysctl_kern_proc_env`](https://github.com/freebsd/freebsd-src/blob/releng/11.0/sys/kern/kern_proc.c) copies a process's start-up strings, not later `setenv` changes. Only the calling process's pid is accepted (`ESRCH` otherwise, -1 included); a write returns `EPERM`. PS5VR (PPSA99177) reads it for its own pid at start-up.
- [kern.proc.vmmap](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctl` {`CTL_KERN`, `KERN_PROC`, `KERN_PROC_VMMAP`, pid} returns packed FreeBSD 11 `kinfo_vmentry` records (144 bytes each, an empty path) for the used ranges of the guest address-space arena, in address order, as [`kinfo_getvmmap`](https://github.com/freebsd/freebsd-src/blob/releng/11.0/lib/libutil/kinfo_getvmmap.c) reads them. A range is one record: the type is always `KVME_TYPE_DEFAULT`, the protection is `sceKernelVirtualQuery`'s for the range's first page (0 where it reports none), and offset, object, resident page counts, flags and path are 0. Only the calling process's pid is accepted (`ESRCH` otherwise, -1 included, as FreeBSD's `pget` gives); a write returns `EPERM` and a short buffer `ENOMEM`. Ranges outside the arena, which guest code cannot map, are not reported. The walk takes the arena lock once per range, not for the whole walk as FreeBSD holds the map lock, so an `mmap` or `munmap` on another thread can change the list between two records. Whether a PS5 application may read its own map, and what types and paths the console reports, is unverified
- [sysctl names a PS5 payload process gets ENOENT for](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - a write to these names throws (except `machdep.cacheline_size`, `ENOENT` as on FreeBSD amd64), `sysctlnametomib` returns `ENOENT` for `kern.smp.maxcpus`, `kern.stackprot`, `kern.sched.name` and `kern.timecounter.hardware`, which the console maps to MIBs above 2147482800; a title process was not measured. A payload process on a PS5 (firmware 12.70, and 13.60 for four names) gets -1 with `ENOENT` by name for `hw.byteorder`, `hw.cacheline_size`, `hw.floatingpoint`, `hw.machine_arch`, `hw.usermem`, `kern.bootfile`, `kern.clockrate`, `kern.cp_time`, `kern.ident`, `kern.maxfiles`, `kern.maxfilesperproc`, `kern.maxproc`, `kern.ps_strings` and those four, and by MIB for {1, 6}, {1, 7}, {1, 12}, {1, 27}, {1, 32}, {6, 4} and {6, 11}, as here. `hw.physmem`, `hw.realmem` and `vm.overcommit` report values here