From 60755acef05be51046912c045dd3962fc69f94ef Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 21 Mar 2024 22:45:08 -0700 Subject: [PATCH] FEXLoader: Add some debug-only tracking for FEX owned FDs I remember seeing some application last year where they closed a FEX owned FD but now I don't remember what it was. This can really mess us up so add some debug tracking so we can try and find it again. Might be something specifically around flatpack, appimage, or chrome's sandbox. I have some ideas about how to work around these problems if they crop up but need to find the problem applications again. --- Source/Tools/FEXLoader/FEXLoader.cpp | 13 +++++- .../LinuxSyscalls/FileManagement.cpp | 18 ++++++++ .../LinuxSyscalls/FileManagement.h | 46 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/Source/Tools/FEXLoader/FEXLoader.cpp b/Source/Tools/FEXLoader/FEXLoader.cpp index 7814818add..481229b1e7 100644 --- a/Source/Tools/FEXLoader/FEXLoader.cpp +++ b/Source/Tools/FEXLoader/FEXLoader.cpp @@ -62,7 +62,7 @@ desc: Glues the ELF loader, FEXCore and LinuxSyscalls to launch an elf under fex namespace { static bool SilentLog; -static int OutputFD {STDERR_FILENO}; +static int OutputFD {-1}; static bool ExecutedWithFD {false}; void MsgHandler(LogMan::DebugLevels Level, char const *Message) { @@ -88,7 +88,7 @@ void AssertHandler(char const *Message) { } // Anonymous namespace namespace FEXServerLogging { - int FEXServerFD{}; + int FEXServerFD{-1}; void MsgHandler(LogMan::DebugLevels Level, char const *Message) { FEXServerClient::MsgHandler(FEXServerFD, Level, Message); } @@ -456,6 +456,15 @@ int main(int argc, char **argv, char **const envp) { auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get()) : FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), std::move(Allocator)); + // Now that we have the syscall handler. Track some FDs that are FEX owned. + if (OutputFD != -1) { + SyscallHandler->FM.TrackFEXFD(OutputFD); + } + SyscallHandler->FM.TrackFEXFD(FEXServerClient::GetServerFD()); + if (FEXServerLogging::FEXServerFD != -1) { + SyscallHandler->FM.TrackFEXFD(FEXServerLogging::FEXServerFD); + } + { // Load VDSO in to memory prior to mapping our ELFs. void* VDSOBase = FEX::VDSO::LoadVDSOThunks(Loader.Is64BitMode(), SyscallHandler.get()); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp index 1b5e4c3cff..4fafd612c9 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.cpp @@ -250,6 +250,9 @@ FileManager::FileManager(FEXCore::Context::Context *ctx) if (RootFSFD == -1) { RootFSFD = AT_FDCWD; } + else { + TrackFEXFD(RootFSFD); + } } fextl::unordered_map ThunkDB; @@ -571,6 +574,13 @@ uint64_t FileManager::Open(const char *pathname, int flags, uint32_t mode) { } uint64_t FileManager::Close(int fd) { +#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED + if (CheckIfFDInTrackedSet(fd)) { + LogMan::Msg::EFmt("{} closing FEX FD {}", __func__, fd); + RemoveFEXFD(fd); + } +#endif + return ::close(fd); } @@ -578,6 +588,14 @@ uint64_t FileManager::CloseRange(unsigned int first, unsigned int last, unsigned #ifndef CLOSE_RANGE_CLOEXEC #define CLOSE_RANGE_CLOEXEC (1U << 2) #endif +#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED + if (!(flags & CLOSE_RANGE_CLOEXEC) && + CheckIfFDRangeInTrackedSet(first, last)) { + LogMan::Msg::EFmt("{} closing FEX FDs in range ({}, {})", __func__, first, last); + RemoveFEXFDRange(first, last); + } +#endif + return ::syscall(SYSCALL_DEF(close_range), first, last, flags); } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.h index 42e59f7679..547b60abfe 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/FileManagement.h @@ -88,7 +88,53 @@ class FileManager final { bool SupportsProcFSInterpreterPath() const { return SupportsProcFSInterpreter; } +#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED + void TrackFEXFD(int FD) noexcept { + std::lock_guard lk(FEXTrackingFDMutex); + FEXTrackingFDs.emplace(FD); + } + + void RemoveFEXFD(int FD) noexcept { + std::lock_guard lk(FEXTrackingFDMutex); + FEXTrackingFDs.erase(FD); + } + + void RemoveFEXFDRange(int begin, int end) noexcept { + std::lock_guard lk(FEXTrackingFDMutex); + + std::erase_if(FEXTrackingFDs, [begin, end](int FD) { + return FD >= begin && (FD <= end || end == -1); + }); + } + + bool CheckIfFDInTrackedSet(int FD) noexcept { + std::lock_guard lk(FEXTrackingFDMutex); + return FEXTrackingFDs.contains(FD); + } + + bool CheckIfFDRangeInTrackedSet(int begin, int end) noexcept { + std::lock_guard lk(FEXTrackingFDMutex); + // Just linear scan since the number of tracking FDs is low. + for (auto it : FEXTrackingFDs) { + if (it >= begin && (it <= end || end == -1)) return true; + } + return false; + } + +#else + void TrackFEXFD(int FD) const noexcept {} + bool CheckIfFDInTrackedSet(int FD) const noexcept { return false; } + void RemoveFEXFD(int FD) const noexcept {} + void RemoveFEXFDRange(int begin, int end) const noexcept {} + bool CheckIfFDRangeInTrackedSet(int begin, int end) const noexcept { return false; } +#endif + private: +#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED + std::mutex FEXTrackingFDMutex; + fextl::set FEXTrackingFDs; +#endif + bool RootFSPathExists(const char* Filepath); struct ThunkDBObject {