Files
AnyPS5/core/libs/tests/GuestSystemServiceLoadExecStandardError.cpp
T
Muhammad Wasif Ali WasifandClaude Opus 5.5 fff18d2f3b test(libSceSystemService): accept the write watch diagnostic before the LoadExec child's line
guest_system_load_exec_standard_error compared the host standard error of
the re-executed child with "started <mode>\n" exactly. On a Linux kernel
without asynchronous userfaultfd write protection (before 6.7, such as
WSL2's 6.6) libc prints "[memory] write watch unavailable: ..." to the
host standard error at start-up, so the comparison failed and the test
aborted on every run. That one diagnostic line is now skipped when it
comes first; the rest of the host stream must still be the child's line
exactly, and the guest pipe must still be empty.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01En5cnt6RwCiNXDJwm9t8Tf
2026-10-26 04:22:43 +05:00

84 lines
2.7 KiB
C++

#include "prx/libkernel/include/Api.hpp"
#include "prx/libSceSystemService/include/SystemService.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <string_view>
#include <sys/wait.h>
#include <unistd.h>
namespace {
constexpr const char* Marker = "APS5_LOAD_EXEC_STANDARD_ERROR";
constexpr const char* AfterLoadExec = "--after-load-exec";
void Require(bool value) {
if (!value) std::abort();
}
std::string Drain(int descriptor) {
std::string text;
char buffer[4096];
for (ssize_t count; (count = ::read(descriptor, buffer, sizeof(buffer))) > 0;) text.append(buffer, static_cast<std::size_t>(count));
::close(descriptor);
return text;
}
bool HostOutputIs(const std::string& text, const std::string& line) {
constexpr std::string_view WriteWatchUnavailable = "[memory] write watch unavailable: ";
std::size_t begin = 0;
if (text.compare(0, WriteWatchUnavailable.size(), WriteWatchUnavailable) == 0) {
const std::size_t end = text.find('\n');
if (end == std::string::npos) return false;
begin = end + 1;
}
return text.compare(begin, std::string::npos, line) == 0;
}
void Run(const char* mode, const char* const* arguments) {
int host[2] = {-1, -1};
int guest[2] = {-1, -1};
Require(::pipe(host) == 0);
Require(::pipe(guest) == 0);
const pid_t child = ::fork();
Require(child >= 0);
if (child == 0) {
::close(host[0]);
::close(guest[0]);
if (::dup2(host[1], 2) != 2) ::_exit(70);
::close(host[1]);
if (dup2_nid_postfix(guest[1], 2) != 2) ::_exit(71);
::close(guest[1]);
if (::setenv(Marker, mode, 1) != 0) ::_exit(72);
sceSystemServiceLoadExec("/app0/eboot.bin", arguments);
::_exit(73);
}
::close(host[1]);
::close(guest[1]);
const std::string hostText = Drain(host[0]);
const std::string guestText = Drain(guest[0]);
int status = 0;
Require(::waitpid(child, &status, 0) == child);
Require(WIFEXITED(status) && WEXITSTATUS(status) == 0);
Require(HostOutputIs(hostText, std::string("started ") + mode + "\n"));
Require(guestText.empty());
}
}
int main(int argc, char** argv) {
if (const char* mode = std::getenv(Marker); mode != nullptr) {
if (std::strcmp(mode, "list") == 0) Require(argc == 1 && std::strcmp(argv[0], AfterLoadExec) == 0);
else Require(std::strcmp(mode, "none") == 0 && argc == 1);
std::fprintf(stderr, "started %s\n", mode);
return 0;
}
const char* const arguments[] = {AfterLoadExec, nullptr};
Run("list", arguments);
Run("none", nullptr);
std::puts("LoadExec standard error tests passed");
return 0;
}