Files
AnyPS5/core/libs/tests/GuestHostStreams.cpp
T
SynnZer0andClaude Opus 5.5 8029428497 fix(libkernel): keep host standard streams open on guest close
Guest descriptors are host C runtime descriptors. Closing descriptor 0, 1
or 2 closed the host process's standard stream, and the runtime then gave
that number to the next file opened anywhere in the process, including
files libkernel opens itself, such as the APR reader's. The title's next
close of the same number closed that file mid-read.

close and sceKernelClose now fail with EBADF for descriptors 0 to 2 that
were open when libkernel loaded, and leave them open. A standard
descriptor that was not open at load can still be opened and closed by
the guest.

PPSA21703 calls sceKernelClose(0) repeatedly while loading asset
bundles, which made APR reads return 0 or a part of the requested bytes
and raised UCRT invalid parameter reports from close.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:46:42 +02:00

39 lines
1.1 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdlib>
extern "C" {
int APS5_VABI dup_nid_postfix(int);
int APS5_VABI pipe_nid_postfix(int*);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI sceKernelClose(int);
int* APS5_VABI __error_nid_postfix();
}
static void Require(bool value) { if (!value) std::abort(); }
constexpr int GuestEbadf = 9;
constexpr int SceKernelErrorEbadf = static_cast<int>(0x80020009);
static bool Open(int fd) {
const int duplicate = dup_nid_postfix(fd);
if (duplicate < 0) return false;
Require(close_nid_postfix(duplicate) == 0);
return true;
}
int main() {
bool open[3];
for (int fd = 0; fd < 3; ++fd) open[fd] = Open(fd);
for (int fd = 0; fd < 3; ++fd) {
if (!open[fd]) continue;
Require(close_nid_postfix(fd) == -1 && *__error_nid_postfix() == GuestEbadf);
Require(sceKernelClose(fd) == SceKernelErrorEbadf);
Require(Open(fd));
}
int pipe[2];
Require(pipe_nid_postfix(pipe) == 0);
for (const int fd : pipe) Require(fd >= 3 || !open[fd]);
Require(close_nid_postfix(pipe[0]) == 0 && close_nid_postfix(pipe[1]) == 0);
return 0;
}