FEXCore: Fixes ERROR_AND_DIE

ERROR_AND_DIE was using __builtin_trap which would send our application
either a SIGILL or SIGTRAP depending on architecture.
This would then be captured by our faulting system and passed over to
the guest application.

If the guest application happened to have a signal handler installed for
these then it would pick up this fault and potentially continue
unsafely.

Now we can remove this usage of __builtin_trap and switch over to our
own handler.

Our frontend will check to see if the fault came from our handler and
uninstall the host signal handlers in this case. Which is what we want
for "ERROR_AND_DIE"
This commit is contained in:
Ryan Houdek committed 2021-11-19 12:05:30 -08:00
1 parent 3dc938b8c5
commit 47d04ae807
6 files changed
+49 -2

No files matched your search

+1
View File
@@ -142,6 +142,7 @@ set (SRCS
Utils/Allocator.cpp
Utils/Allocator/64BitAllocator.cpp
Utils/FileLoading.cpp
Utils/ForcedAssert.cpp
Utils/LogManager.cpp
Utils/Telemetry.cpp
Utils/Threads.cpp
+14
View File
@@ -0,0 +1,14 @@
namespace FEXCore::Assert {
// This function can not be inlined
[[noreturn]]
__attribute__((noinline, naked))
void ForcedAssert() {
#ifdef _M_X86_64
asm volatile("ud2");
#else
asm volatile("hlt #1");
#endif
}
}
+7 -2
View File
@@ -1,5 +1,4 @@
#pragma once
// Contains general abstractions related to compilers used to build FEX.
// Specifies the minimum alignment for a variable or structure field, measured in bytes.
@@ -21,9 +20,15 @@
#define FEX_PACKED __attribute__((packed))
// Causes execution to exit abnormally.
#define FEX_TRAP_EXECUTION __builtin_trap()
#define FEX_TRAP_EXECUTION FEXCore::Assert::ForcedAssert()
// Dictates to the compiler that the path this is on should not be reachable
// from normal execution control flow. If normal execution does reach this,
// then program behavior is undefined.
#define FEX_UNREACHABLE __builtin_unreachable()
namespace FEXCore::Assert {
// This function can not be inlined
[[noreturn]]
FEX_DEFAULT_VISIBILITY void ForcedAssert();
}
+20
View File
@@ -49,6 +49,7 @@ $end_info$
#include <fmt/format.h>
#include <sys/sysinfo.h>
#include <sys/signal.h>
namespace {
static bool SilentLog;
@@ -534,6 +535,25 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Context::InitializeContext(CTX);
auto SignalDelegation = std::make_unique<FEX::HLE::SignalDelegator>();
SignalDelegation->RegisterFrontendHostSignalHandler(SIGILL, [&SignalDelegation](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
ucontext_t* _context = (ucontext_t*)ucontext;
auto &mcontext = _context->uc_mcontext;
uint64_t PC{};
#ifdef _M_ARM_64
PC = mcontext.pc;
#else
PC = mcontext.gregs[REG_RIP];
#endif
if (PC == reinterpret_cast<uint64_t>(&FEXCore::Assert::ForcedAssert)) {
// This is a host side assert. Don't deliver this to the guest
// We want to actually break here
SignalDelegation->UninstallHostHandler(Signal);
return true;
}
return false;
}, true);
auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX, SignalDelegation.get())
: FEX::HLE::x32::CreateHandler(CTX, SignalDelegation.get(), std::move(Allocator));
@@ -241,6 +241,12 @@ namespace FEX::HLE {
return true;
}
void SignalDelegator::UninstallHostHandler(int Signal) {
SignalHandler &SignalHandler = HostHandlers[Signal];
::syscall(SYS_rt_sigaction, Signal, &SignalHandler.OldAction, nullptr, 8);
}
SignalDelegator::SignalDelegator() {
// Register this delegate
LOGMAN_THROW_A(!GlobalDelegator, "Can't register global delegator multiple times!");
@@ -55,6 +55,7 @@ namespace FEX::HLE {
uint64_t GuestSignalFD(int fd, const uint64_t *set, size_t sigsetsize , int flags);
/** @} */
void UninstallHostHandler(int Signal);
protected:
// Called from the thunk handler to handle the signal
void HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, int Signal, void *Info, void *UContext) override;