Merge pull request #5772 from DavidSamir/fix/libc-empty-guest-environment

fix(libc): start the guest environment empty instead of copying the host's
This commit is contained in:
Adrià authored and GitHub committed 2026-10-16 21:23:35 +02:00
commit d96e0e554c
3 files changed
+13 -22

No files matched your search

+1 -18
View File
@@ -9,10 +9,6 @@
#include <vector>
#include "prx/libc/include/general/VabiMacros.hpp"
#ifndef _WIN32
extern char** environ;
#endif
namespace {
struct EnvironmentEntry {
std::unique_ptr<std::string> owned;
@@ -26,20 +22,7 @@ struct EnvironmentEntry {
};
std::mutex environmentMutex;
std::vector<EnvironmentEntry>& Environment() {
// Seed once from the host. Guest mutations remain local to the guest runtime.
static std::vector<EnvironmentEntry> entries = [] {
std::vector<EnvironmentEntry> result;
#ifdef _WIN32
char** source = _environ;
#else
char** source = environ;
#endif
if (source) for (; *source; ++source) {
if (**source && **source != '=' && std::strchr(*source, '='))
result.push_back({std::make_unique<std::string>(*source), nullptr});
}
return result;
}();
static std::vector<EnvironmentEntry> entries;
return entries;
}
bool ValidEnvironmentName(const char* name) {
+11 -4
View File
@@ -13,18 +13,25 @@ static void Require(bool value) { if (!value) std::abort(); }
int main() {
const char* key = "ANYPS5_GUEST_ENV_TEST_4C27";
#ifdef _WIN32
Require(_putenv_s(key, "inherited") == 0);
Require(_putenv_s(key, "host") == 0);
#else
Require(::setenv(key, "inherited", 1) == 0);
Require(::setenv(key, "host", 1) == 0);
#endif
Require(std::strcmp(getenv_nid_postfix(key), "inherited") == 0);
Require(getenv_nid_postfix(key) == nullptr);
#ifdef _WIN32
Require(_putenv_s("HOME", "C:\\Users\\host") == 0);
#else
Require(::setenv("HOME", "/home/host", 1) == 0);
#endif
for (const char* host : {"HOME", "PATH"}) Require(std::getenv(host) != nullptr && getenv_nid_postfix(host) == nullptr);
Require(setenv_nid_postfix(key, "inherited", 0) == 0);
Require(setenv_nid_postfix(key, "kept out", 0) == 0);
Require(std::strcmp(getenv_nid_postfix(key), "inherited") == 0);
char value[] = "copied";
Require(setenv_nid_postfix(key, value, 1) == 0);
value[0] = 'X';
Require(std::strcmp(getenv_nid_postfix(key), "copied") == 0);
Require(std::strcmp(std::getenv(key), "inherited") == 0);
Require(std::strcmp(std::getenv(key), "host") == 0);
Require(setenv_nid_postfix(key, "", 1) == 0);
Require(getenv_nid_postfix(key) && *getenv_nid_postfix(key) == 0);
char borrowed[] = "ANYPS5_GUEST_ENV_TEST_4C27=one";
+1
View File
@@ -50,6 +50,7 @@
- [sinhf](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the `sinhf` of the `libc.prx` bundled with PPSA28319 (disassembly): FreeBSD msun's [`e_sinhf.c`](https://github.com/freebsd/freebsd-src/blob/5aa554c7e5b6fd9042986df07f12ac5687e41392/lib/msun/src/e_sinhf.c) from before `__ldexp_expf`, which squares `expf(|x| / 2)` between `logf(FLT_MAX)` and the overflow threshold, calling msun's [`expm1f`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/s_expm1f.c) and [`expf`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/e_expf.c), whose paths for the arguments it passes are inlined here. Past the threshold Sony's error helper sets `errno` to `ERANGE` before returning `x * 1e37`; msun itself sets no `errno`. That code, run on a Windows host, gives the same bits as this implementation for every finite argument up to the threshold, of both signs; it was not run on Linux. That the system `libSceLibcInternal`, which Ruffle (PPSA68091) and RetroArch (PPSA99169) import while bundling a zero-return `libc.prx`, has the same code is assumed
- [asinl](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the `asinl` of the `libc.prx` bundled with PPSA28319 (disassembly), FreeBSD msun's [`e_asinl.c`](https://github.com/freebsd/freebsd-src/blob/9f0432041b8fb69832f10c32da50a67f34feeb61/lib/msun/src/e_asinl.c) with the ld80 [`invtrig.c`](https://github.com/freebsd/freebsd-src/blob/9f0432041b8fb69832f10c32da50a67f34feeb61/lib/msun/ld80/invtrig.c) coefficients, which the binary holds as the same 80-bit constants. For |x| > 1, the infinities and NaN, Sony's error helper raises `FE_INVALID` and sets `errno` to `EDOM` before returning `(x - x) / (x - x)`, so a NaN argument also sets `EDOM`; msun itself sets no `errno`. 20 million random arguments in (-1, 1) give the same bits as that code run on a Windows host. The result needs the x87 precision control at 64 bits, as FreeBSD amd64 starts threads (control word `0x37f`), but guest threads do not set it: on Windows a thread started by `_beginthreadex` runs at 53 bits (`0x27f`), where `asinl(0.75)` returns mantissa `0xd91a98ae3406e000` instead of `0xd91a98ae3406e041`. The system `libSceLibcInternal` that RetroArch (PPSA99169) imports is assumed to have the same code
- [atanhf](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the `atanhf` of the `libc.prx` bundled with PPSA28319 (disassembly): FreeBSD msun's [`e_atanhf.c`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/e_atanhf.c) calling msun's [`log1pf`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/s_log1pf.c), whose paths for the positive arguments it passes are inlined here; the exported `log1pf` is still the host's. For |x| > 1, the infinities and NaN, Sony's error helper raises `FE_INVALID` and sets `errno` to `EDOM` before returning `(x - x) / (x - x)`, and for |x| = 1 it raises `FE_DIVBYZERO` and sets `errno` to `ERANGE` before returning `x / 0`; msun itself sets no `errno`. That code, run on a Windows host, gives the same bits, `errno` and floating-point exceptions as this implementation for all 2^32 arguments; it was not run on Linux. That the system `libSceLibcInternal`, which Dusklight (PPSA99305) imports while bundling a zero-return `libc.prx`, has the same code is assumed
- [getenv, setenv, unsetenv, putenv](../../../core/libs/prx/libc/src/Environment.cpp) (libc) keep one guest environment that starts empty, like the `environ` libkernel exports, and never reads or changes the host's. A title sees only the variables it sets itself; host variables such as `HOME` and `PATH` name host paths a guest cannot use (PS5MC, PPSA99420, exits with `XBMC_Run returned -1` when it gets the host's `HOME`). Whether the console starts a title with any variables set is not measured
- On macOS the [write watch](../../../core/libs/prx/libc/src/GuestWriteWatch.cpp) write-protects watched pages and records the first write to each from a SIGBUS/SIGSEGV handler, because macOS has no counterpart to `userfaultfd` write-protect or `MEM_WRITE_WATCH`. The protection is real, so:
- a system call that writes into a protected page fails with `EFAULT` instead of faulting. `GuestArena::HostWrite` lifts the protection for the calls it brackets (file reads and socket receives); a host call that writes guest memory without it (`clock_gettime` and the like into a watched page) fails;
- code that changes the protection of watched pages, or maps over them, has to call `GuestWriteWatchProtectionChanged`, and code that asks the kernel for a page's protection has to ask `GuestWriteWatchProtection` first, since a watched page is read-only between a collection and the next write. Nothing calls either yet: the macOS memory paths are not in the tree;