From cc8fa9d9341d5861cac7f750c6ff582c7908a3bc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 19 Jun 2021 07:23:25 -0700 Subject: [PATCH 1/2] Fixes 32bit sysinfo syscall This was altered in kernel 2.3.23 to include a mem_unit variable. mem_unit states what each of the memory units is scaled by. The kernel will always have this be 1 or page size on x86, so follow that behaviour. Instead of maxing out the values to what uint32_t can handle, scale all of these by page size instead. This fixes a crash in KOTOR2's boot sequence where it would divide by zero if everything was maxed out. --- Source/Tests/LinuxSyscalls/x32/Info.cpp | 33 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/x32/Info.cpp b/Source/Tests/LinuxSyscalls/x32/Info.cpp index 32053c438..3c664586e 100644 --- a/Source/Tests/LinuxSyscalls/x32/Info.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Info.cpp @@ -28,8 +28,9 @@ namespace FEX::HLE::x32 { uint32_t freeswap; uint16_t procs; uint32_t totalhigh; + uint32_t freehigh; uint32_t mem_unit; - char _pad[12]; + char _pad[8]; }; static_assert(sizeof(sysinfo32) == 64, "Needs to be 64bytes"); @@ -49,16 +50,32 @@ namespace FEX::HLE::x32 { if (Result != -1) { #define Copy(x) info->x = static_castx)>(std::min(Host.x, static_cast(std::numeric_limitsx)>::max()))); Copy(uptime); + Copy(procs); +#define CopyShift(x) info->x = static_castx)>(Host.x >> ShiftAmount); + info->loads[0] = std::min(Host.loads[0], static_cast(std::numeric_limits::max())); info->loads[1] = std::min(Host.loads[1], static_cast(std::numeric_limits::max())); info->loads[2] = std::min(Host.loads[2], static_cast(std::numeric_limits::max())); - Copy(totalram); - Copy(sharedram); - Copy(bufferram); - Copy(totalswap); - Copy(freeswap); - Copy(procs); - Copy(totalhigh); + + // If any result can't fit in to a uint32_t then we need to shift the mem_unit and all the members + // Set the mem_unit to the pagesize + uint32_t ShiftAmount{}; + if ((Host.totalram >> 32) != 0 || + (Host.totalswap >> 32) != 0) { + + while (Host.mem_unit < 4096) { + Host.mem_unit <<= 1; + ++ShiftAmount; + } + } + + CopyShift(totalram); + CopyShift(sharedram); + CopyShift(bufferram); + CopyShift(totalswap); + CopyShift(freeswap); + CopyShift(totalhigh); + CopyShift(freehigh); Copy(mem_unit); } SYSCALL_ERRNO(); From 1745bcceb631e32f3c0f963a490b0ca0338d25c4 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 19 Jun 2021 07:26:41 -0700 Subject: [PATCH 2/2] Fixes 32bit statfs and fstatfs These 32bit syscalls use a compat statfs which is only 64bytes in size. This was overwriting data on the guest stack and causing crashes. Describe a 32-bit statfs struct and ensure with struct verifier that it matches. Fixes a crash in KOTOR2 that would happen just before the main menu. --- Source/Tests/LinuxSyscalls/Syscalls/FS.cpp | 11 ----- Source/Tests/LinuxSyscalls/x32/FD.cpp | 18 +++++++ Source/Tests/LinuxSyscalls/x32/Types.h | 56 ++++++++++++++++++++++ Source/Tests/LinuxSyscalls/x64/FD.cpp | 11 +++++ 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/Syscalls/FS.cpp b/Source/Tests/LinuxSyscalls/Syscalls/FS.cpp index 6020c8dcc..587ad037e 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/FS.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/FS.cpp @@ -15,7 +15,6 @@ $end_info$ #include #include #include -#include #include #include #include @@ -110,16 +109,6 @@ namespace FEX::HLE { #endif }); - REGISTER_SYSCALL_IMPL(statfs, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, struct statfs *buf) -> uint64_t { - uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statfs(path, buf); - SYSCALL_ERRNO(); - }); - - REGISTER_SYSCALL_IMPL(fstatfs, [](FEXCore::Core::CpuStateFrame *Frame, int fd, struct statfs *buf) -> uint64_t { - uint64_t Result = ::fstatfs(fd, buf); - SYSCALL_ERRNO(); - }); - REGISTER_SYSCALL_IMPL(truncate, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, off_t length) -> uint64_t { uint64_t Result = ::truncate(path, length); SYSCALL_ERRNO(); diff --git a/Source/Tests/LinuxSyscalls/x32/FD.cpp b/Source/Tests/LinuxSyscalls/x32/FD.cpp index 75d704f72..28417a0c1 100644 --- a/Source/Tests/LinuxSyscalls/x32/FD.cpp +++ b/Source/Tests/LinuxSyscalls/x32/FD.cpp @@ -174,6 +174,24 @@ namespace FEX::HLE::x32 { SYSCALL_ERRNO(); }); + REGISTER_SYSCALL_IMPL_X32(statfs, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, statfs32_32 *buf) -> uint64_t { + struct statfs host_stat; + uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statfs(path, &host_stat); + if (Result != -1) { + *buf = host_stat; + } + SYSCALL_ERRNO(); + }); + + REGISTER_SYSCALL_IMPL_X32(fstatfs, [](FEXCore::Core::CpuStateFrame *Frame, int fd, statfs32_32 *buf) -> uint64_t { + struct statfs host_stat; + uint64_t Result = ::fstatfs(fd, &host_stat); + if (Result != -1) { + *buf = host_stat; + } + SYSCALL_ERRNO(); + }); + REGISTER_SYSCALL_IMPL_X32(fstatfs64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, size_t sz, struct statfs64_32 *buf) -> uint64_t { LOGMAN_THROW_A(sz == sizeof(struct statfs64_32), "This needs to match"); diff --git a/Source/Tests/LinuxSyscalls/x32/Types.h b/Source/Tests/LinuxSyscalls/x32/Types.h index a02a470ff..b9ac022e9 100644 --- a/Source/Tests/LinuxSyscalls/x32/Types.h +++ b/Source/Tests/LinuxSyscalls/x32/Types.h @@ -471,6 +471,62 @@ statfs64_32 { static_assert(std::is_trivial::value, "Needs to be trivial"); static_assert(sizeof(statfs64_32) == 84, "Incorrect size"); +struct +FEX_ANNOTATE("alias-x86_32-statfs") +FEX_ANNOTATE("fex-match") +statfs32_32 { + int32_t f_type; + int32_t f_bsize; + int32_t f_blocks; + int32_t f_bfree; + int32_t f_bavail; + int32_t f_files; + int32_t f_ffree; + __kernel_fsid_t f_fsid; + int32_t f_namelen; + int32_t f_frsize; + int32_t f_flags; + int32_t pad[4]; + + statfs32_32() = delete; + + statfs32_32(struct statfs host) { + #define COPY(x) x = host.x + COPY(f_type); + COPY(f_bsize); + COPY(f_blocks); + COPY(f_bfree); + COPY(f_bavail); + COPY(f_files); + COPY(f_ffree); + COPY(f_namelen); + COPY(f_frsize); + COPY(f_flags); + + memcpy(&f_fsid, &host.f_fsid, sizeof(f_fsid)); + #undef COPY + } + + statfs32_32(struct statfs64 host) { + #define COPY(x) x = host.x + COPY(f_type); + COPY(f_bsize); + COPY(f_blocks); + COPY(f_bfree); + COPY(f_bavail); + COPY(f_files); + COPY(f_ffree); + COPY(f_namelen); + COPY(f_frsize); + COPY(f_flags); + + memcpy(&f_fsid, &host.f_fsid, sizeof(f_fsid)); + #undef COPY + } +}; +static_assert(std::is_trivial::value, "Needs to be trivial"); +static_assert(sizeof(statfs32_32) == 64, "Incorrect size"); + struct FEX_ANNOTATE("alias-x86_32-flock") FEX_ANNOTATE("fex-match") diff --git a/Source/Tests/LinuxSyscalls/x64/FD.cpp b/Source/Tests/LinuxSyscalls/x64/FD.cpp index cb58513bd..3ac65bb28 100644 --- a/Source/Tests/LinuxSyscalls/x64/FD.cpp +++ b/Source/Tests/LinuxSyscalls/x64/FD.cpp @@ -15,6 +15,7 @@ $end_info$ #include #include #include +#include #include namespace FEX::HLE::x64 { @@ -234,5 +235,15 @@ namespace FEX::HLE::x64 { uint64_t Result = ::dup2(oldfd, newfd); SYSCALL_ERRNO(); }); + + REGISTER_SYSCALL_IMPL_X64(statfs, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, struct statfs *buf) -> uint64_t { + uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statfs(path, buf); + SYSCALL_ERRNO(); + }); + + REGISTER_SYSCALL_IMPL_X64(fstatfs, [](FEXCore::Core::CpuStateFrame *Frame, int fd, struct statfs *buf) -> uint64_t { + uint64_t Result = ::fstatfs(fd, buf); + SYSCALL_ERRNO(); + }); } }