From 5fd00e31bb82a8d29f7daf63455a657fc19f32ca Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 27 Jul 2022 11:57:23 +0200 Subject: [PATCH] Thunks/X11: Distinguish between host and guest pointers in XFree This function must be able to handle both guest heap pointers *and* host heap pointers, so it only forwards to the native host library for the latter. This is because Xlibint users allocate memory using internal macros aliasing to libc's malloc but then they free using the function XFree. For libX11, this is not a problem since the allocation happens in a thunked API function (and hence on the host heap), but if a function from an unthunked library accesses Xlibint, it will allocate on the guest heap. One notable example where this was encountered is XF86VidModeGetAllModeLines. --- .../Source/Interface/HLE/Thunks/Thunks.cpp | 22 ++++++++++ External/jemalloc | 2 +- ThunkLibs/include/common/Guest.h | 11 +++++ ThunkLibs/libX11/libX11_Guest.cpp | 41 +++++++++++++++++-- ThunkLibs/libX11/libX11_interface.cpp | 4 +- 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index cbcddc200..464ced06a 100644 --- a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -28,6 +28,8 @@ $end_info$ #include #include +#include "jemalloc/jemalloc.h" + struct LoadlibArgs { const char *Name; }; @@ -108,6 +110,11 @@ namespace FEXCore { { 0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff }, &IsLibLoaded }, + { + // sha256(fex:is_host_heap_allocation) + { 0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e }, + &IsHostHeapAllocation + }, { // sha256(fex:link_address_to_function) { 0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77 }, @@ -288,6 +295,21 @@ namespace FEXCore { ThunkHandler->GuestcallToHostTrampoline[gci] = args->rv; } + /** + * Checks if the given pointer is allocated on the host heap. + * + * This is useful for thunking APIs that need to work with both guest + * and host heap pointers. + */ + static void IsHostHeapAllocation(void* ArgsRV) { + struct ArgsRV_t { + void* ptr; + bool rv; + } *args = reinterpret_cast(ArgsRV); + + args->rv = je_is_known_allocation(args->ptr); + } + static void LoadLib(void *ArgsV) { auto CTX = Thread->CTX; diff --git a/External/jemalloc b/External/jemalloc index b700fc403..3b08599cf 160000 --- a/External/jemalloc +++ b/External/jemalloc @@ -1 +1 @@ -Subproject commit b700fc403054dd6cee370d88f79f8b6403846e1c +Subproject commit 3b08599cfa23526c46c849f5a9e96e2e3bb9aa5a diff --git a/ThunkLibs/include/common/Guest.h b/ThunkLibs/include/common/Guest.h index 637fbb9d9..69b736d84 100644 --- a/ThunkLibs/include/common/Guest.h +++ b/ThunkLibs/include/common/Guest.h @@ -33,6 +33,7 @@ struct LoadlibArgs { MAKE_THUNK(fex, loadlib, "0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80") MAKE_THUNK(fex, is_lib_loaded, "0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff") +MAKE_THUNK(fex, is_host_heap_allocation, "0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e") MAKE_THUNK(fex, link_address_to_function, "0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77") MAKE_THUNK(fex, make_host_trampoline_for_guest_function, "0x1e, 0x51, 0x6b, 0x07, 0x39, 0xeb, 0x50, 0x59, 0xb3, 0xf3, 0x4f, 0xca, 0xdd, 0x58, 0x37, 0xe9, 0xf0, 0x30, 0xe5, 0x89, 0x81, 0xc7, 0x14, 0xfb, 0x24, 0xf9, 0xba, 0xe7, 0x0e, 0x00, 0x1e, 0x86") @@ -201,3 +202,13 @@ inline Target *MakeHostTrampolineForGuestFunction(uint8_t HostPacker[32], void ( return (Target *)argsrv.rv; } + +inline bool IsHostHeapAllocation(void* ptr) { + struct { + void* ptr; + bool rv; + } args = { ptr, {} }; + + fexthunks_fex_is_host_heap_allocation(&args); + return args.rv; +} diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index 6e35c002b..0df8e060e 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -9,6 +9,11 @@ $end_info$ #include #include +// Include Xlibint.h and undefine some of its macros that clash with the standard library +#include +#undef min +#undef max + #include #include #include @@ -68,16 +73,44 @@ extern "C" { return rv; } - static void LockMutexFunction() { + static void LockMutexFunction(LockInfoPtr) { fprintf(stderr, "libX11: LockMutex\n"); } - static void UnlockMutexFunction() { + static void UnlockMutexFunction(LockInfoPtr) { fprintf(stderr, "libX11: LockMutex\n"); } - void (*_XLockMutex_fn)() = LockMutexFunction; - void (*_XUnlockMutex_fn)() = UnlockMutexFunction; + int XFree(void* ptr) { + // This function must be able to handle both guest heap pointers *and* host heap pointers, + // so it only forwards to the native host library for the latter. + // + // This is because Xlibint users allocate memory using internal macros aliasing to libc's + // malloc but then they free using the function XFree. For libX11, this is not a problem since + // the allocation happens in a thunked API function (and hence on the host heap), but if a + // function from an unthunked library accesses Xlibint, it will allocate on the guest heap. + // + // One notable example where this was encountered is XF86VidModeGetAllModeLines. + + if (!ptr || IsHostHeapAllocation(ptr)) { + return fexfn_pack_XFree(ptr); + } else { + free(ptr); + return 1; + } + } + + void XFreeEventData(Display* display, XGenericEventCookie* cookie) { + // Has the same heap-mismatch issue as XFree, so we have to reimplement it manually + if (_XIsEventCookie(display, (XEvent*)cookie) && cookie->data) { + XFree(cookie->data); + cookie->data = nullptr; + cookie->cookie = 0; + } + } + + void (*_XLockMutex_fn)(LockInfoPtr) = LockMutexFunction; + void (*_XUnlockMutex_fn)(LockInfoPtr) = UnlockMutexFunction; typedef struct _LockInfoRec *LockInfoPtr; LockInfoPtr _Xglobal_lock = (LockInfoPtr)0x4142434445464748ULL; } diff --git a/ThunkLibs/libX11/libX11_interface.cpp b/ThunkLibs/libX11/libX11_interface.cpp index d3bedf97c..9f6e71fd7 100644 --- a/ThunkLibs/libX11/libX11_interface.cpp +++ b/ThunkLibs/libX11/libX11_interface.cpp @@ -465,7 +465,7 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; -template<> struct fex_gen_config {}; +template<> struct fex_gen_config : fexgen::custom_guest_entrypoint {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -502,7 +502,7 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; -template<> struct fex_gen_config {}; +template<> struct fex_gen_config : fexgen::custom_guest_entrypoint {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {};