mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-14 05:00:41 +02:00
Windows: Extend user handle access masks in callbacks where necessary
While most applications will just create ALL_ACCESS handles which support the additional operations FEX needs over the regular windows syscall (usually just QUERY_INFORMATION to lookup the thread object), it is valid for them to pass in the minimal set required for each operation and windows/wine will reject any other operations (e.g. querying the TEB base on a handle with only the SUSPEND right). Windows permits duplicating handles to extend their permissions given the process itself has such permissions, so do that as necessary for the operations FEX needs.
This commit is contained in:
1 parent
b2b5ccf69c
commit
1ac3d3c5a6
4 files changed
+79
-30
No files matched your search
@@ -581,8 +581,10 @@ void BTCpuThreadTerm(HANDLE Thread, LONG ExitCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME);
|
||||
|
||||
THREAD_BASIC_INFORMATION Info;
|
||||
if (auto Err = NtQueryInformationThread(Thread, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
|
||||
if (auto Err = NtQueryInformationThread(*ThreadDup, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -590,10 +592,10 @@ void BTCpuThreadTerm(HANDLE Thread, LONG ExitCode) {
|
||||
bool Self = ThreadTID == GetCurrentThreadId();
|
||||
if (!Self) {
|
||||
// If we are suspending a thread that isn't ourselves, try to suspend it first so we know internal JIT locks aren't being held.
|
||||
RtlWow64SuspendThread(Thread, NULL);
|
||||
RtlWow64SuspendThread(*ThreadDup, NULL);
|
||||
}
|
||||
|
||||
auto [Err, TLS] = GetThreadTLS(Thread);
|
||||
auto [Err, TLS] = GetThreadTLS(*ThreadDup);
|
||||
if (Err) {
|
||||
return;
|
||||
}
|
||||
@@ -628,44 +630,53 @@ void* __wine_get_unix_opcode() {
|
||||
}
|
||||
|
||||
NTSTATUS BTCpuGetContext(HANDLE Thread, HANDLE Process, void* Unknown, WOW64_CONTEXT* Context) {
|
||||
auto [Err, TLS] = GetThreadTLS(Thread);
|
||||
if (!FEX::Windows::ValidateHandleAccess(Thread, THREAD_GET_CONTEXT)) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||
auto [Err, TLS] = GetThreadTLS(*ThreadDup);
|
||||
if (Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
if (!(TLS.ControlWord().load(std::memory_order::relaxed) & ControlBits::WOW_CPU_AREA_DIRTY)) {
|
||||
if (Err = Context::FlushThreadStateContext(Thread); Err) {
|
||||
if (Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
}
|
||||
|
||||
return RtlWow64GetThreadContext(Thread, Context);
|
||||
return RtlWow64GetThreadContext(*ThreadDup, Context);
|
||||
}
|
||||
|
||||
NTSTATUS BTCpuSetContext(HANDLE Thread, HANDLE Process, void* Unknown, WOW64_CONTEXT* Context) {
|
||||
auto [Err, TLS] = GetThreadTLS(Thread);
|
||||
if (!FEX::Windows::ValidateHandleAccess(Thread, THREAD_SET_CONTEXT)) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||
auto [Err, TLS] = GetThreadTLS(*ThreadDup);
|
||||
if (Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
|
||||
// Back-up the input context incase we've been passed the CPU area (the flush below would wipe it out otherwise)
|
||||
WOW64_CONTEXT TmpContext = *Context;
|
||||
|
||||
if (!(TLS.ControlWord().load(std::memory_order::relaxed) & ControlBits::WOW_CPU_AREA_DIRTY)) {
|
||||
if (Err = Context::FlushThreadStateContext(Thread); Err) {
|
||||
if (Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the input context into the CPU area then pass the full context into the JIT
|
||||
if (Err = RtlWow64SetThreadContext(Thread, &TmpContext); Err) {
|
||||
if (Err = RtlWow64SetThreadContext(*ThreadDup, &TmpContext); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
TmpContext.ContextFlags = WOW64_CONTEXT_FULL | WOW64_CONTEXT_EXTENDED_REGISTERS;
|
||||
|
||||
if (Err = RtlWow64GetThreadContext(Thread, &TmpContext); Err) {
|
||||
if (Err = RtlWow64GetThreadContext(*ThreadDup, &TmpContext); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
@@ -712,8 +723,9 @@ NTSTATUS BTCpuSuspendLocalThread(HANDLE Thread, ULONG* Count) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||
THREAD_BASIC_INFORMATION Info;
|
||||
if (NTSTATUS Err = NtQueryInformationThread(Thread, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
|
||||
if (NTSTATUS Err = NtQueryInformationThread(*ThreadDup, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
@@ -723,17 +735,17 @@ NTSTATUS BTCpuSuspendLocalThread(HANDLE Thread, ULONG* Count) {
|
||||
// Mark the CPU area as dirty, to force the JIT context to be restored from it on entry as it may be changed using
|
||||
// SetThreadContext (which doesn't use the BTCpu API)
|
||||
if (!(GetTLS().ControlWord().fetch_or(ControlBits::WOW_CPU_AREA_DIRTY, std::memory_order::relaxed) & ControlBits::WOW_CPU_AREA_DIRTY)) {
|
||||
if (NTSTATUS Err = Context::FlushThreadStateContext(Thread); Err) {
|
||||
if (NTSTATUS Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
}
|
||||
|
||||
return NtSuspendThread(Thread, Count);
|
||||
return NtSuspendThread(*ThreadDup, Count);
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Suspending thread: {:X}", ThreadTID);
|
||||
|
||||
auto [Err, TLS] = GetThreadTLS(Thread);
|
||||
auto [Err, TLS] = GetThreadTLS(*ThreadDup);
|
||||
if (Err) {
|
||||
return Err;
|
||||
}
|
||||
@@ -742,7 +754,8 @@ NTSTATUS BTCpuSuspendLocalThread(HANDLE Thread, ULONG* Count) {
|
||||
|
||||
// If the thread hasn't yet been initialized, suspend it without special handling as it wont yet have entered the JIT
|
||||
if (!Threads.contains(ThreadTID)) {
|
||||
return NtSuspendThread(Thread, Count);
|
||||
LogMan::Msg::DFmt("Thread suspended: {:X}", ThreadTID);
|
||||
return NtSuspendThread(*ThreadDup, Count);
|
||||
}
|
||||
|
||||
// If CONTROL_IN_JIT is unset at this point, then it can never be set (and thus the JIT cannot be reentered) as
|
||||
@@ -762,7 +775,7 @@ NTSTATUS BTCpuSuspendLocalThread(HANDLE Thread, ULONG* Count) {
|
||||
;
|
||||
|
||||
// The JIT has now been interrupted and the context stored in the thread's CPU area is up-to-date
|
||||
if (Err = NtSuspendThread(Thread, Count); Err) {
|
||||
if (Err = NtSuspendThread(*ThreadDup, Count); Err) {
|
||||
TLS.ControlWord().fetch_and(~ControlBits::PAUSED, std::memory_order::relaxed);
|
||||
return Err;
|
||||
}
|
||||
@@ -773,12 +786,12 @@ NTSTATUS BTCpuSuspendLocalThread(HANDLE Thread, ULONG* Count) {
|
||||
|
||||
// NtSuspendThread may return before the thread is actually suspended, so a sync operation like NtGetContextThread
|
||||
// needs to be called to ensure it is before we unset CONTROL_PAUSED
|
||||
std::ignore = NtGetContextThread(Thread, &TmpContext);
|
||||
std::ignore = NtGetContextThread(*ThreadDup, &TmpContext);
|
||||
|
||||
// Mark the CPU area as dirty, to force the JIT context to be restored from it on entry as it may be changed using
|
||||
// SetThreadContext (which doesn't use the BTCpu API)
|
||||
if (!(TLS.ControlWord().fetch_or(ControlBits::WOW_CPU_AREA_DIRTY, std::memory_order::relaxed) & ControlBits::WOW_CPU_AREA_DIRTY)) {
|
||||
if (Err = Context::FlushThreadStateContext(Thread); Err) {
|
||||
if (Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user