mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 00:53:02 -05:00
XPSemu PPSA97358 imports both from libkernel.prx; with the imports ahead of them covered, its predicted next stops (relink registry order, not an observed run) are: FAIL: unresolved ELF import KaK52lYTBIs from libkernel.prx (execv, 7 call sites) FAIL: unresolved ELF import kdguLiAheLI from libkernel.prx (execve, 4 call sites) Today the title stops earlier at the observed FAIL: unresolved ELF import aRo9AhFUXcM from libkernel.prx (sigsetjmp, merged in #3486) As for execvp (#2827), the process image cannot be replaced here, so both throw with the execvp message pattern once FreeBSD 11's execve would start loading the image (freebsd-src 8c639945, sys/kern/kern_exec.c exec_copyin_args, do_execve, exec_check_permissions; lib/libc/gen/exec.c execv passes environ). Before that they fail as FreeBSD does: EFAULT for a null argument vector or path, ENOENT and ENOTDIR from the lookup as stat reports it, EACCES for a path that is not a regular file. Paths of PATH_MAX bytes or more and strings over ARG_MAX throw instead of guessing the console's limits. The execv calls in XPSemu's sources run in a fork child (net/tap.c), and its fork fails with ENOSYS (ZiZc3/XPSemu 8eec07b4, ps5/compat/libc_missing.c, ps5/build-title.sh --defsym). NIDs computed with tools/nid_names.py and confirmed in aerolib. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 57f0fa37132bc6c78bc02282044a00e2db859484)
92 lines
4.4 KiB
C++
92 lines
4.4 KiB
C++
#include "prx/libc/include/General.hpp"
|
|
#include <chrono>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
extern "C" {
|
|
int APS5_VABI execvp_nid_postfix(const char*, char* const*);
|
|
int APS5_VABI execv_nid_postfix(const char*, char* const*);
|
|
int APS5_VABI execve_nid_postfix(const char*, char* const*, char* const*);
|
|
int* APS5_VABI __error_nid_postfix();
|
|
}
|
|
static constexpr int GUEST_ENOENT = 2;
|
|
static constexpr int GUEST_EACCES = 13;
|
|
static constexpr int GUEST_EFAULT = 14;
|
|
static constexpr int GUEST_ENOTDIR = 20;
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
static bool ReportsNotImplemented(const char* file, char* const* arguments) {
|
|
try {
|
|
execvp_nid_postfix(file, arguments);
|
|
} catch (const std::runtime_error& error) {
|
|
return std::string(error.what()) == "execvp: executable replacement not implemented";
|
|
}
|
|
return false;
|
|
}
|
|
template <typename TCall> static bool Throws(TCall call, const std::string& message) {
|
|
try {
|
|
call();
|
|
} catch (const std::runtime_error& error) {
|
|
return error.what() == message;
|
|
}
|
|
return false;
|
|
}
|
|
template <typename TCall> static bool Fails(TCall call, int error) {
|
|
*__error_nid_postfix() = 0;
|
|
return call() == -1 && *__error_nid_postfix() == error;
|
|
}
|
|
static void RequireBoth(const char* path, char* const* arguments, int error) {
|
|
Require(Fails([&] { return execv_nid_postfix(path, arguments); }, error));
|
|
Require(Fails([&] { return execve_nid_postfix(path, arguments, nullptr); }, error));
|
|
}
|
|
int main() {
|
|
char shell[] = "sh";
|
|
char option[] = "-c";
|
|
char command[] = "exit 0";
|
|
char* arguments[] = {shell, option, command, nullptr};
|
|
Require(ReportsNotImplemented("sh", arguments));
|
|
Require(ReportsNotImplemented("/bin/sh", arguments));
|
|
Require(ReportsNotImplemented(nullptr, nullptr));
|
|
|
|
const auto name = "anyps5-exec-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
|
|
const auto directory = std::filesystem::temp_directory_path() / name;
|
|
Require(std::filesystem::create_directory(directory));
|
|
{ std::ofstream output(directory / "program"); output << "program"; }
|
|
AddPathAlias_nid_no_patch("/exec-alias", directory.string().c_str());
|
|
char variable[] = "HOME=/";
|
|
char* environment[] = {variable, nullptr};
|
|
|
|
RequireBoth(nullptr, arguments, GUEST_EFAULT);
|
|
RequireBoth("/exec-alias/program", nullptr, GUEST_EFAULT);
|
|
RequireBoth(nullptr, nullptr, GUEST_EFAULT);
|
|
RequireBoth("/exec-alias/missing", arguments, GUEST_ENOENT);
|
|
RequireBoth("/exec-alias/missing/program", arguments, GUEST_ENOENT);
|
|
RequireBoth("", arguments, GUEST_ENOENT);
|
|
RequireBoth("/exec-alias/program/", arguments, GUEST_ENOTDIR);
|
|
RequireBoth("/exec-alias", arguments, GUEST_EACCES);
|
|
RequireBoth("/exec-alias/", arguments, GUEST_EACCES);
|
|
|
|
Require(Throws([&] { execv_nid_postfix("/exec-alias/program", arguments); }, "execv: executable replacement not implemented"));
|
|
Require(Throws([&] { execve_nid_postfix("/exec-alias/program", arguments, environment); }, "execve: executable replacement not implemented"));
|
|
Require(Throws([&] { execve_nid_postfix("/exec-alias/program", arguments, nullptr); }, "execve: executable replacement not implemented"));
|
|
|
|
std::string fittingPath = "/exec-alias/";
|
|
while (fittingPath.size() < 1016) fittingPath += "./";
|
|
fittingPath += "missing";
|
|
Require(fittingPath.size() == 1023);
|
|
RequireBoth(fittingPath.c_str(), arguments, GUEST_ENOENT);
|
|
const std::string longPath = fittingPath + "s";
|
|
Require(Throws([&] { execv_nid_postfix(longPath.c_str(), arguments); }, "execv: path of PATH_MAX bytes or more"));
|
|
Require(Throws([&] { execve_nid_postfix(longPath.c_str(), arguments, nullptr); }, "execve: path of PATH_MAX bytes or more"));
|
|
|
|
std::string fitting(262143, 'a');
|
|
char* fittingArguments[] = {fitting.data(), nullptr};
|
|
RequireBoth("/exec-alias/missing", fittingArguments, GUEST_ENOENT);
|
|
char* overflowing[] = {fitting.data(), variable, nullptr};
|
|
Require(Throws([&] { execv_nid_postfix("/exec-alias/missing", overflowing); }, "execv: arguments and environment exceed ARG_MAX"));
|
|
Require(Throws([&] { execve_nid_postfix("/exec-alias/missing", fittingArguments, environment); }, "execve: arguments and environment exceed ARG_MAX"));
|
|
|
|
RemovePathAlias_nid_no_patch("/exec-alias");
|
|
std::filesystem::remove_all(directory);
|
|
}
|