diff --git a/Source/Common/Config.cpp b/Source/Common/Config.cpp index 2e44fbabf..7c4353a0b 100644 --- a/Source/Common/Config.cpp +++ b/Source/Common/Config.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,9 @@ #include #include +#include +#include + namespace FEX::Config { namespace JSON { static void LoadJSonConfig(const fextl::string& Config, std::function Func) { @@ -494,20 +498,66 @@ void LoadConfig(fextl::string ProgramName, char** const envp, const PortableInfo } #ifndef _WIN32 -const char* FindUserHomeThroughUID() { - auto passwd = getpwuid(geteuid()); - if (passwd) { - return passwd->pw_dir; +fextl::string FindUserHomeThroughUID() { + // `getpwuid` allocates memory, parse `/etc/passwd` manually. + // Format is trivial: `::::::` + + fextl::vector Data; + if (!FEXCore::FileLoading::LoadFile(Data, "/etc/passwd")) { + return {}; } - return nullptr; + + auto to_string_view = [](auto rng) { + return std::string_view(&*rng.begin(), ranges::distance(rng)); + }; + + const auto uid = geteuid(); + + for (const auto entry : ranges::views::split(Data, '\n') | ranges::views::transform(to_string_view)) { + const auto elements = ranges::views::split(entry, ':') | ranges::views::transform(to_string_view); + // Reject bad entries. + if (std::distance(elements.begin(), elements.end()) != 7) { + continue; + } + + auto iter = elements.begin(); + ++iter; // name + ++iter; // password hash + ++iter; // comment + // uid + const auto uid_s = *iter; + ++iter; + ++iter; // gid + // home + const auto home_s = *iter; + ++iter; + ++iter; // shell + + uint64_t element_uid; + auto Results = std::from_chars(uid_s.begin(), uid_s.end(), element_uid, 10); + + // Error parsing. + if (Results.ptr == uid_s.begin()) { + continue; + } + + if (element_uid == uid) { + return fextl::string(home_s); + } + } + + return {}; } -const char* GetHomeDirectory() { +fextl::string GetHomeDirectory() { const char* HomeDir = getenv("HOME"); // Try to get home directory from uid if (!HomeDir || !FHU::Filesystem::Exists(HomeDir)) { - HomeDir = FindUserHomeThroughUID(); + auto UIDHome = FindUserHomeThroughUID(); + if (!UIDHome.empty() && FHU::Filesystem::Exists(UIDHome)) { + return UIDHome; + } } // try the PWD @@ -523,7 +573,7 @@ const char* GetHomeDirectory() { return HomeDir; } #else -const char* GetHomeDirectory() { +fextl::string GetHomeDirectory() { const char* HomeDir = getenv("WINEHOMEDIR"); if (HomeDir) { // Skip over the \??\ prefix in the NT path since we want a DOS path @@ -560,7 +610,7 @@ fextl::string GetDataDirectory(bool Global, const PortableInformation& PortableI return GLOBAL_DATA_DIRECTORY; } - const char* HomeDir = GetHomeDirectory(); + auto HomeDir = GetHomeDirectory(); const char* DataXDG = getenv("XDG_DATA_HOME"); const fextl::string LegacyDir = fextl::string {HomeDir} + "/.fex-emu/"; @@ -607,7 +657,7 @@ fextl::string GetConfigDirectory(bool Global, const PortableInformation& Portabl return GLOBAL_DATA_DIRECTORY; } - const char* HomeDir = GetHomeDirectory(); + auto HomeDir = GetHomeDirectory(); const char* ConfigXDG = getenv("XDG_CONFIG_HOME"); const fextl::string LegacyDir = fextl::string {HomeDir} + "/.fex-emu/"; @@ -644,7 +694,7 @@ fextl::string GetCacheDirectory() { } #endif - const char* HomeDir = GetHomeDirectory(); + auto HomeDir = GetHomeDirectory(); const char* CacheXDG = getenv("XDG_CACHE_HOME"); return (CacheXDG ? fextl::string {CacheXDG} : (fextl::string {HomeDir} + "/.cache")) + "/fex-emu/"; #else diff --git a/Source/Common/Config.h b/Source/Common/Config.h index f988400e0..3ba689315 100644 --- a/Source/Common/Config.h +++ b/Source/Common/Config.h @@ -57,7 +57,7 @@ ApplicationNames GetApplicationNames(const fextl::vector& Args, b */ void LoadConfig(fextl::string ProgramName = {}, char** const envp = nullptr, const PortableInformation& PortableInfo = {}); -const char* GetHomeDirectory(); +fextl::string GetHomeDirectory(); fextl::string GetDataDirectory(const PortableInformation& PortableInfo); fextl::string GetConfigDirectory(bool Global, const PortableInformation& PortableInfo); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp index 0f11aa6a2..8cc6ce4aa 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp @@ -106,7 +106,7 @@ void FileManager::LoadThunkDatabase(fextl::unordered_map + +#include +#include +#include +#include +#include +#include + +static std::vector LoadFile(const char* Path) { + int fd = open(Path, O_RDONLY); + REQUIRE(fd != -1); + + struct stat st {}; + REQUIRE(fstat(fd, &st) != -1); + + std::vector Result {}; + Result.resize(st.st_size); + + size_t DidRead {}; + do { + auto Read = read(fd, Result.data() + DidRead, Result.size() - DidRead); + + if (Read == -1) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + REQUIRE(errno != 0); + } + + DidRead += Read; + } while (DidRead != st.st_size); + + return Result; +} + +TEST_CASE("execveat - memfd - MFD_CLOEXEC") { + auto MapsFile = LoadFile("/usr/bin/true"); + REQUIRE(MapsFile.size() != 0); + + int fd = memfd_create("Anonymous", MFD_CLOEXEC | MFD_ALLOW_SEALING); + REQUIRE(fd != -1); + + size_t Written {}; + do { + auto Wrote = write(fd, MapsFile.data() + Written, MapsFile.size() - Written); + if (Wrote == -1) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + REQUIRE(errno != 0); + } + Written += Wrote; + } while (Written != MapsFile.size()); + + const char* argv[] = {"tmp", nullptr}; + auto Res = ::syscall(SYS_execveat, fd, "", argv, nullptr, AT_EMPTY_PATH); + + // Will only get here if execveat fails. + close(fd); + REQUIRE(Res == 0); +}