mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-01-19 04:42:27 +00:00
Linux: Implements a fault safe memcpy routine
We are required in our syscall emulation to handle cases where pointers are invalid. This means we need to pessimistically assume a memcpy will fault when reading application memory. This implements a signal handler based approach to catching the SIGSEGV on memcpy and returning an EFAULT if it faults.
This commit is contained in:
parent
f956f008ea
commit
c5ffc0664d
@ -4,6 +4,7 @@ set (SRCS
|
||||
VDSO_Emulation.cpp
|
||||
LinuxSyscalls/GdbServer.cpp
|
||||
LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp
|
||||
LinuxSyscalls/FaultSafeMemcpy.cpp
|
||||
LinuxSyscalls/FileManagement.cpp
|
||||
LinuxSyscalls/LinuxAllocator.cpp
|
||||
LinuxSyscalls/NetStream.cpp
|
||||
|
@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "LinuxSyscalls/Syscalls.h"
|
||||
|
||||
namespace FEX::HLE::FaultSafeMemcpy {
|
||||
#ifdef _M_ARM_64
|
||||
__attribute__((naked))
|
||||
size_t CopyFromUser(void *Dest, const void* Src, size_t Size) {
|
||||
__asm volatile(R"(
|
||||
// Early exit if a memcpy of size zero.
|
||||
cbz x2, 2f;
|
||||
|
||||
1:
|
||||
.globl CopyFromUser_FaultInst
|
||||
CopyFromUser_FaultInst:
|
||||
ldrb w3, [x1], 1; // <- This line can fault.
|
||||
strb w3, [x0], 1;
|
||||
sub x2, x2, 1;
|
||||
cbnz x2, 1b;
|
||||
2:
|
||||
mov x0, 0;
|
||||
ret;
|
||||
)"
|
||||
::: "memory");
|
||||
}
|
||||
|
||||
__attribute__((naked))
|
||||
size_t CopyToUser(void *Dest, const void* Src, size_t Size) {
|
||||
__asm volatile(R"(
|
||||
// Early exit if a memcpy of size zero.
|
||||
cbz x2, 2f;
|
||||
|
||||
1:
|
||||
ldrb w3, [x1], 1;
|
||||
.globl CopyToUser_FaultInst
|
||||
CopyToUser_FaultInst:
|
||||
strb w3, [x0], 1; // <- This line can fault.
|
||||
sub x2, x2, 1;
|
||||
cbnz x2, 1b;
|
||||
2:
|
||||
mov x0, 0;
|
||||
ret;
|
||||
)"
|
||||
::: "memory");
|
||||
}
|
||||
|
||||
extern "C" uint64_t CopyFromUser_FaultInst;
|
||||
void * const CopyFromUser_FaultLocation = &CopyFromUser_FaultInst;
|
||||
|
||||
extern "C" uint64_t CopyToUser_FaultInst;
|
||||
void * const CopyToUser_FaultLocation = &CopyToUser_FaultInst;
|
||||
|
||||
bool IsFaultLocation(uint64_t PC) {
|
||||
return reinterpret_cast<void*>(PC) == CopyFromUser_FaultLocation ||
|
||||
reinterpret_cast<void*>(PC) == CopyToUser_FaultLocation;
|
||||
}
|
||||
|
||||
#else
|
||||
size_t CopyFromUser(void *Dest, const void* Src, size_t Size) {
|
||||
memcpy(Dest, Src, Size);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t CopyToUser(void *Dest, const void* Src, size_t Size) {
|
||||
memcpy(Dest, Src, Size);
|
||||
return Size;
|
||||
}
|
||||
|
||||
bool IsFaultLocation(uint64_t PC) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
@ -7,6 +7,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "LinuxSyscalls/SignalDelegator.h"
|
||||
#include "LinuxSyscalls/Syscalls.h"
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
@ -1467,6 +1468,14 @@ namespace FEX::HLE {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (Signal == SIGSEGV &&
|
||||
SigInfo.si_code == SEGV_ACCERR &&
|
||||
FaultSafeMemcpy::IsFaultLocation(ArchHelpers::Context::GetPc(UContext))) {
|
||||
// Return from the subroutine, returning EFAULT.
|
||||
ArchHelpers::Context::SetArmReg(UContext, 0, EFAULT);
|
||||
ArchHelpers::Context::SetPc(UContext, ArchHelpers::Context::GetArmReg(UContext, 30));
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (IsAsyncSignal(&SigInfo, Signal) && MustDeferSignal) {
|
||||
// If the signal is asynchronous (as determined by si_code) and FEX is in a state of needing
|
||||
|
@ -531,6 +531,16 @@ static bool HasSyscallError(const void* Result) {
|
||||
|
||||
template<bool IncrementOffset, typename T>
|
||||
uint64_t GetDentsEmulation(int fd, T *dirp, uint32_t count);
|
||||
|
||||
namespace FaultSafeMemcpy {
|
||||
// These are little helper functions for cases when FEX needs to copy data to or from the application in a robust fashion.
|
||||
// CopyFromUser and CopyToUser are memcpy routines that expect to safely SIGSEGV when reading or writing application memory respectively.
|
||||
// Returns zero if the memcpy completed, or crashes with SIGABRT and a log message if it faults.
|
||||
[[nodiscard]] size_t CopyFromUser(void *Dest, const void* Src, size_t Size);
|
||||
[[nodiscard]] size_t CopyToUser(void *Dest, const void* Src, size_t Size);
|
||||
bool IsFaultLocation(uint64_t PC);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Registers syscall for both 32bit and 64bit
|
||||
|
Loading…
x
Reference in New Issue
Block a user