From f444edc19e67ef0a903728804f50e13ccbfac16e Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Thu, 28 Apr 2016 18:14:36 +0800
Subject: [PATCH] ntdll: Implement NtSetLdtEntries.

---
 dlls/kernel32/tests/thread.c  | 79 +++++++++++++++++++++++++++++++++++
 dlls/ntdll/unix/signal_i386.c |  2 +-
 2 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c
index a9869863b44..2c2a0fda843 100644
--- a/dlls/kernel32/tests/thread.c
+++ b/dlls/kernel32/tests/thread.c
@@ -99,6 +99,7 @@ static BOOL (WINAPI *pSetThreadGroupAffinity)(HANDLE,const GROUP_AFFINITY*,GROUP
 static NTSTATUS (WINAPI *pNtSetInformationThread)(HANDLE,THREADINFOCLASS,LPCVOID,ULONG);
 static HRESULT (WINAPI *pSetThreadDescription)(HANDLE,const WCHAR *);
 static HRESULT (WINAPI *pGetThreadDescription)(HANDLE,WCHAR **);
+static NTSTATUS (WINAPI *pNtSetLdtEntries)(ULONG,ULONG,ULONG,ULONG,ULONG,ULONG);
 
 static HANDLE create_target_process(const char *arg)
 {
@@ -1278,6 +1279,82 @@ 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);
+        todo_wine
+        ok(ret, "GetThreadSelectorEntry failed\n");
+        todo_wine
+        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);
+        todo_wine
+        ok(ret, "GetThreadSelectorEntry failed\n");
+        todo_wine
+        ok(!memcmp(&ds_entry, &sel.entry, sizeof(ds_entry)), "entries do not match\n");
+    }
+}
+
 #endif  /* __i386__ */
 
 static HANDLE finish_event;
@@ -2340,6 +2417,7 @@ static void init_funcs(void)
        X(NtQueryInformationThread);
        X(RtlGetThreadErrorMode);
        X(NtSetInformationThread);
+       X(NtSetLdtEntries);
    }
 #undef X
 }
@@ -2396,6 +2474,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 320ffa68407..a52490a096b 100644
--- a/dlls/ntdll/unix/signal_i386.c
+++ b/dlls/ntdll/unix/signal_i386.c
@@ -480,7 +480,7 @@ NTSTATUS CDECL get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG
                 if (reply->flags)
                     info->Entry = ldt_make_entry( (void *)reply->base, reply->limit, reply->flags );
                 else
-                    status = STATUS_UNSUCCESSFUL;
+                    status = STATUS_ACCESS_VIOLATION;
             }
         }
         SERVER_END_REQ;
-- 
2.26.2