From 42ab95dc1f3de8bd0e35d6878b2144d756d2ec99 Mon Sep 17 00:00:00 2001 From: Elizabeth Figura Date: Tue, 16 Sep 2025 13:45:16 -0500 Subject: [PATCH] ntdll-NtSetLdtEntries: Restore the parts that were not yet upstreamed. --- ...0001-ntdll-Implement-NtSetLdtEntries.patch | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 patches/ntdll-NtSetLdtEntries/0001-ntdll-Implement-NtSetLdtEntries.patch diff --git a/patches/ntdll-NtSetLdtEntries/0001-ntdll-Implement-NtSetLdtEntries.patch b/patches/ntdll-NtSetLdtEntries/0001-ntdll-Implement-NtSetLdtEntries.patch new file mode 100644 index 00000000..c87bca20 --- /dev/null +++ b/patches/ntdll-NtSetLdtEntries/0001-ntdll-Implement-NtSetLdtEntries.patch @@ -0,0 +1,143 @@ +From f677f11f611d59a85e2919d599f90cb9d454b5e9 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Thu, 28 Apr 2016 18:14:36 +0800 +Subject: [PATCH] ntdll/tests: Add some tests for NtSetLdtEntries. + +1. Some copy protections call NtSetLdtEntries(0x000f) and then with 'retf' +instruction jump to that selector expecting that it works (the tests show that +NtSetLdtEntries(0x000f,0x001f) should succeed). In order to make this work a +limitation to set only selectors > LDT_FIRST_ENTRY (512) in wine_ldt_set_entry() +was removed. + +2. wine_ldt_set_entry() was made to always mark modified selector entries as +WINE_LDT_FLAGS_ALLOCATED, otherwise get_selector_entry() server call returns +entries without that flag set and NtQueryInformationThread(ThreadDescriptorTableEntry) +fails. +--- + dlls/kernel32/tests/thread.c | 75 +++++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/signal_i386.c | 2 +- + 2 files changed, 76 insertions(+), 1 deletion(-) + +diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c +index c0303b471e8..64b62fefd0d 100644 +--- a/dlls/kernel32/tests/thread.c ++++ b/dlls/kernel32/tests/thread.c +@@ -103,6 +103,7 @@ static HRESULT (WINAPI *pSetThreadDescription)(HANDLE,const WCHAR *); + static HRESULT (WINAPI *pGetThreadDescription)(HANDLE,WCHAR **); + static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG,PVECTORED_EXCEPTION_HANDLER); + static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID); ++static NTSTATUS (WINAPI *pNtSetLdtEntries)(ULONG,ULONG,ULONG,ULONG,ULONG,ULONG); + + static HANDLE create_target_process(const char *arg) + { +@@ -1319,6 +1320,78 @@ static void test_GetThreadSelectorEntry(void) + ok(entry.HighWord.Bits.Granularity == 1, "expected 1, got %u\n", entry.HighWord.Bits.Granularity); + } + ++static void test_NtSetLdtEntries(void) ++{ ++ THREAD_DESCRIPTOR_INFORMATION tdi; ++ LDT_ENTRY ds_entry; ++ CONTEXT ctx; ++ DWORD ret; ++ union ++ { ++ LDT_ENTRY entry; ++ DWORD dw[2]; ++ } sel; ++ ++ if (!pNtSetLdtEntries) ++ { ++ win_skip("NtSetLdtEntries is not available on this platform\n"); ++ return; ++ } ++ ++ if (pNtSetLdtEntries(0, 0, 0, 0, 0, 0) == STATUS_NOT_IMPLEMENTED) /* WoW64 */ ++ { ++ win_skip("NtSetLdtEntries is not implemented on this platform\n"); ++ return; ++ } ++ ++ ret = pNtSetLdtEntries(0, 0, 0, 0, 0, 0); ++ ok(!ret, "NtSetLdtEntries failed: %08x\n", ret); ++ ++ ctx.ContextFlags = CONTEXT_SEGMENTS; ++ ret = GetThreadContext(GetCurrentThread(), &ctx); ++ ok(ret, "GetThreadContext failed\n"); ++ ++ tdi.Selector = ctx.SegDs; ++ ret = pNtQueryInformationThread(GetCurrentThread(), ThreadDescriptorTableEntry, &tdi, sizeof(tdi), &ret); ++ ok(!ret, "NtQueryInformationThread failed: %08x\n", ret); ++ ds_entry = tdi.Entry; ++ ++ tdi.Selector = 0x000f; ++ ret = pNtQueryInformationThread(GetCurrentThread(), ThreadDescriptorTableEntry, &tdi, sizeof(tdi), &ret); ++ ok(ret == STATUS_ACCESS_VIOLATION, "got %08x\n", ret); ++ ++ tdi.Selector = 0x001f; ++ ret = pNtQueryInformationThread(GetCurrentThread(), ThreadDescriptorTableEntry, &tdi, sizeof(tdi), &ret); ++ ok(ret == STATUS_ACCESS_VIOLATION, "NtQueryInformationThread returned %08x\n", ret); ++ ++ ret = GetThreadSelectorEntry(GetCurrentThread(), 0x000f, &sel.entry); ++ ok(!ret, "GetThreadSelectorEntry should fail\n"); ++ ++ ret = GetThreadSelectorEntry(GetCurrentThread(), 0x001f, &sel.entry); ++ ok(!ret, "GetThreadSelectorEntry should fail\n"); ++ ++ memset(&sel.entry, 0x9a, sizeof(sel.entry)); ++ ret = GetThreadSelectorEntry(GetCurrentThread(), ctx.SegDs, &sel.entry); ++ ok(ret, "GetThreadSelectorEntry failed\n"); ++ ok(!memcmp(&ds_entry, &sel.entry, sizeof(ds_entry)), "entries do not match\n"); ++ ++ ret = pNtSetLdtEntries(0x000f, sel.dw[0], sel.dw[1], 0x001f, sel.dw[0], sel.dw[1]); ++ ok(!ret || broken(ret == STATUS_INVALID_LDT_DESCRIPTOR) /*XP*/, "NtSetLdtEntries failed: %08x\n", ret); ++ ++ if (!ret) ++ { ++ memset(&sel.entry, 0x9a, sizeof(sel.entry)); ++ ret = GetThreadSelectorEntry(GetCurrentThread(), 0x000f, &sel.entry); ++ ok(ret, "GetThreadSelectorEntry failed\n"); ++ ok(!memcmp(&ds_entry, &sel.entry, sizeof(ds_entry)), "entries do not match\n"); ++ ++ memset(&sel.entry, 0x9a, sizeof(sel.entry)); ++ ret = GetThreadSelectorEntry(GetCurrentThread(), 0x001f, &sel.entry); ++ ok(ret, "GetThreadSelectorEntry failed\n"); ++ ok(!memcmp(&ds_entry, &sel.entry, sizeof(ds_entry)), "entries do not match\n"); ++ } ++} ++ + #endif /* __i386__ */ + + static HANDLE finish_event; +@@ -2644,6 +2717,7 @@ static void init_funcs(void) + X(NtSetInformationThread); + X(RtlAddVectoredExceptionHandler); + X(RtlRemoveVectoredExceptionHandler); ++ X(NtSetLdtEntries); + } + #undef X + } +@@ -2700,6 +2774,7 @@ START_TEST(thread) + test_SetThreadContext(); + test_GetThreadSelectorEntry(); + test_GetThreadContext(); ++ test_NtSetLdtEntries(); + #endif + test_QueueUserWorkItem(); + test_RegisterWaitForSingleObject(); +diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c +index f8a0a9856e6..e0b04a01731 100644 +--- a/dlls/ntdll/unix/signal_i386.c ++++ b/dlls/ntdll/unix/signal_i386.c +@@ -2266,7 +2266,7 @@ NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_ + if (reply->flags) + info->Entry = ldt_make_entry( reply->base, reply->limit, reply->flags ); + else +- status = STATUS_UNSUCCESSFUL; ++ status = STATUS_ACCESS_VIOLATION; + } + } + SERVER_END_REQ; +-- +2.50.1 +