[PATCH 2/4] [NtDll]: NtSetInformationProcess(ProcessLdtInformation)

Eric Pouech eric.pouech at wanadoo.fr
Sat Nov 4 04:52:34 CST 2006


- implemented setting LDT entries through NtSetInformationProcess

A+
---

 dlls/ntdll/process.c        |   44 ++++++++
 dlls/ntdll/tests/selector.c |  232 +++++++++++++++++++++++++++++++++++++++++++
 include/wine/library.h      |    4 +
 libs/wine/ldt.c             |    5 -
 4 files changed, 281 insertions(+), 4 deletions(-)

diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c
index 7b8fcaa..b3b14c6 100644
--- a/dlls/ntdll/process.c
+++ b/dlls/ntdll/process.c
@@ -392,6 +392,50 @@ NTSTATUS WINAPI NtSetInformationProcess(
             SERVER_END_REQ;
         }
         break;
+    case ProcessLdtInformation:
+#ifdef __i386__
+        if (ProcessHandle != GetCurrentProcess())
+        {
+            FIXME("Unsupported on other processes\n");
+            ret = STATUS_NOT_IMPLEMENTED;
+        }
+        else if ((ProcessInformationLength & 7) == 0)
+        {
+            PROCESS_LDT_INFORMATION*    pli = ProcessInformation;
+
+            /* only works for clean LDT indexes */
+            if ((pli->dwSelector & 7) != 0)
+                ret = STATUS_INVALID_LDT_OFFSET;
+            else if (ProcessInformationLength < 2 * sizeof(DWORD) + pli->dwSize)
+                ret = STATUS_INFO_LENGTH_MISMATCH;
+            else if ((pli->dwSize % sizeof(LDT_ENTRY)) != 0)
+                ret = STATUS_INVALID_LDT_SIZE;
+            else
+            {
+                int i;
+                
+                for (i = 0; i < pli->dwSize / sizeof(LDT_ENTRY); i++)
+                {
+                    int ret;
+
+                    wine_ldt_copy.flags[(pli->dwSelector >> 3) + i] |= WINE_LDT_FLAGS_ALLOCATED;
+                    ret = wine_ldt_set_entry(pli->dwSelector + (i << 3) + 4, &pli->ldt[i]);
+                    
+                    if (ret < 0)
+                    {
+                        FIXME("got %d\n", ret);
+                        ret = STATUS_INVALID_PARAMETER; /* FIXME */
+                        break;
+                    }
+                }
+                pli->dwSize = 0;
+            }
+        }
+        else ret = STATUS_INFO_LENGTH_MISMATCH;
+#else
+        ret = STATUS_NOT_IMPLEMENTED;
+#endif
+        break;        
     default:
         FIXME("(%p,0x%08x,%p,0x%08x) stub\n",
               ProcessHandle,ProcessInformationClass,ProcessInformation,
diff --git a/dlls/ntdll/tests/selector.c b/dlls/ntdll/tests/selector.c
index 4d3ea92..0c7402d 100644
--- a/dlls/ntdll/tests/selector.c
+++ b/dlls/ntdll/tests/selector.c
@@ -31,6 +31,72 @@ #ifdef __i386__
  *   LDT size, but we don't handle it yet correctly)
  */
 static int (WINAPI *pNtQueryInformationProcess)(HANDLE,DWORD,VOID*,DWORD,DWORD*);
+static int (WINAPI *pNtSetInformationProcess)(HANDLE,DWORD,VOID*,DWORD);
+
+#if defined(__GNUC__)
+
+#include <setjmp.h>
+static jmp_buf jb;
+
+static EXCEPTION_DISPOSITION foo(EXCEPTION_RECORD* er, void* p, CONTEXT* c, void* s)
+{
+    longjmp(jb, 1);
+}
+
+#define __try0 \
+    __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (foo)); \
+    do { \
+        if (!setjmp(jb)) \
+        {
+#define __except0 \
+        } \
+    } while (0); \
+    __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" : : : "%eax");
+
+static DWORD low_read_mem(WORD sel, DWORD offset)
+{
+    DWORD ret;
+
+    __asm__("pushw %gs");
+    __asm__("movw %0,%%ax" : : "r" (sel) : "memory" );
+    __asm__("movw %ax,%gs");
+    __asm__("movl %0,%%eax" : : "r" (offset) : "memory");
+    __asm__( ".byte 0x65\n\tmovl (%%eax),%0" : "=r"(ret));
+    __asm__("popw %gs");
+    return ret;
+}
+
+#elif defined(_MSC_VER)
+
+static DWORD low_read_mem(WORD sel, DWORD offst)
+{
+    __asm push gs;
+    __asm mov ax,sel;
+    __asm mov gs,ax;
+    __asm mov eax,offst;
+    __asm mov eax,gs:[eax];
+    __asm pop gs;
+}
+
+#define __try0  __try
+#define __except0 __except(TRUE) {};
+
+#else
+#error compiler not supported
+#endif
+
+static BOOL read_mem(WORD sel, DWORD offset, DWORD* val)
+{
+    BOOL        ret = FALSE;
+
+    __try0
+    {
+        *val = low_read_mem(sel, offset);
+        ret = TRUE;
+    }
+    __except0;
+    return ret;
+}
 
 #define __SEL(x,r)      (((x) << 3) | ((r) & 3))
 #define LDT(x,r)        (__SEL((x), (r)) | 4)
@@ -62,6 +128,8 @@ static unsigned find_unused_ldt_entry(vo
     return entry;
 }
 
+static LDT_ENTRY null_entry;
+
 static void test_fail_process(unsigned entry)
 {
     DWORD                       buf[12];
@@ -141,6 +209,168 @@ static void test_fail_process(unsigned e
     ok(dw == 8, "Wrong dw value %d\n", dw);
     ok(pli->dwSize == 0, "Wrong pli->dwSize value %d\n", pli->dwSize);
 }
+
+#if 0
+static void print_ldt(const LDT_ENTRY* le)
+{
+    printf("base=%x\n", le->BaseLow | ((DWORD)le->HighWord.Bytes.BaseMid >> 16) | ((DWORD)le->HighWord.Bytes.BaseHi >> 24));
+    printf("limit=%x\n", le->LimitLow | ((DWORD)le->HighWord.Bits.LimitHi >> 16));
+#define YN(x) ((x)?'Y':'N')
+    printf("Granularity=%c Big=%c System=%c Present=%c Reserved=%d Ring=%d Type=%x\n", 
+           YN(le->HighWord.Bits.Granularity), YN(le->HighWord.Bits.Default_Big), 
+           YN(le->HighWord.Bits.Sys), YN(le->HighWord.Bits.Pres), le->HighWord.Bits.Reserved_0, le->HighWord.Bits.Dpl, le->HighWord.Bits.Type);
+#undef YN
+}
+#endif
+
+/* we need this function as Wine plays some dirty tricks in our back:
+ * - makes its own decision about storing the limit/granularity pair...
+ * - doesn't cache the DPL settings
+ */
+static BOOL compare_ldt(const LDT_ENTRY* le1, const LDT_ENTRY* le2)
+{
+    BOOL ret = (le1->BaseLow | ((DWORD)le1->HighWord.Bytes.BaseMid >> 16) | ((DWORD)le1->HighWord.Bytes.BaseHi >> 24)) ==
+        (le2->BaseLow | ((DWORD)le2->HighWord.Bytes.BaseMid >> 16) | ((DWORD)le2->HighWord.Bytes.BaseHi >> 24)) &&
+        (((le1->LimitLow | (le1->HighWord.Bits.LimitHi << 16)) + 1) << (le1->HighWord.Bits.Granularity ? 12 : 0)) - 1 ==
+        (((le2->LimitLow | (le2->HighWord.Bits.LimitHi << 16)) + 1) << (le2->HighWord.Bits.Granularity ? 12 : 0)) - 1 &&
+        le1->HighWord.Bits.Default_Big == le2->HighWord.Bits.Default_Big &&
+        le1->HighWord.Bits.Sys == le2->HighWord.Bits.Sys &&
+        le1->HighWord.Bits.Pres == le2->HighWord.Bits.Pres &&
+        le1->HighWord.Bits.Reserved_0 == le2->HighWord.Bits.Reserved_0 &&
+        /* FIXME Wine doesn't actually cache it: le1->HighWord.Bits.Dpl == le2->HighWord.Bits.Dpl && */
+        le1->HighWord.Bits.Type == le2->HighWord.Bits.Type;
+
+    return ret;
+}
+
+static void fill_ldt(LDT_ENTRY* le, DWORD base, DWORD npages)
+{
+    le->BaseLow                   = base & 0xFFFF;
+    le->HighWord.Bytes.BaseMid    = base >> 16 & 0xFF;
+    le->HighWord.Bytes.BaseHi     = base >> 24;
+    le->LimitLow                  = npages & 0xFFFF;
+    le->HighWord.Bits.LimitHi     = npages >> 16;
+    le->HighWord.Bits.Granularity = 1;
+    le->HighWord.Bits.Default_Big = 1;
+    le->HighWord.Bits.Reserved_0  = 0;
+    le->HighWord.Bits.Sys         = 0;
+    le->HighWord.Bits.Pres        = 1;
+    le->HighWord.Bits.Dpl         = 3;
+    le->HighWord.Bits.Type        = 27;
+}
+
+static void test_new_ldt(unsigned entry)
+{
+    LDT_ENTRY                   le;
+    DWORD                       test;
+    DWORD                       ret;
+    DWORD                       buf[12];
+    PROCESS_LDT_INFORMATION*    pli = (PROCESS_LDT_INFORMATION*)buf;
+    NTSTATUS                    status;
+    DWORD                       dw;
+
+    /* create a new ldt of one page, which contains test */
+    fill_ldt(&le, (DWORD)&test, 1);
+
+    ok(!read_mem(LDT(entry, 3), 0, &ret), "ldt shouldn't be readable\n");
+
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 4;
+    memcpy(&pli->ldt[0], &le, 8);
+    status = pNtSetInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 16);
+    ok(status == STATUS_INVALID_LDT_SIZE, "invalid status (%x)\n", status);
+    ok(!read_mem(LDT(entry, 3), 0, &ret), "ldt shouldn't be readable\n");
+
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 8;
+    memcpy(&pli->ldt[0], &le, 8);
+    status = pNtSetInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 16);
+    ok(status == STATUS_SUCCESS, "should have set LDT (%x)\n", status);
+
+    test = 0x1234;
+    ok(read_mem(LDT(entry, 3), 0, &ret), "ldt should be readable\n");
+    ok(ret == 0x1234, "Wrong value for read %x\n", ret);
+    test = 0x4321;
+    ok(read_mem(LDT(entry, 3), 0, &ret), "ldt should be readable\n");
+    ok(ret == 0x4321, "Wrong value for read %x\n", ret);
+
+    memset(buf, 0, sizeof(buf));
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 8;
+    dw = 0;
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 16, &dw);
+    ok(status == STATUS_SUCCESS, "Wrong status %x\n", status);
+    ok(dw == 16, "wrong dw value %d\n", dw);
+    ok(compare_ldt(&pli->ldt[0], &le), "Wrong LE value\n");
+    todo_wine ok(pli->dwSize == 16, "wrong pli->dwSize=%d\n", pli->dwSize);
+
+    le.HighWord.Bits.Pres = 0;
+
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 8;
+    memcpy(&pli->ldt[0], &le, 8);
+    status = pNtSetInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 16);
+    ok(status == STATUS_SUCCESS, "should have set LDT (%x)\n", status);
+    ok(!read_mem(LDT(entry, 3), 0, &ret), "ldt shouldn't be readable\n");
+
+    pli->dwSelector = PROC_LDT(entry + 1);
+    pli->dwSize = 8;
+    memcpy(&pli->ldt[0], &le, 8);
+    status = pNtSetInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 16);
+    ok(status == STATUS_SUCCESS, "should have set LDT (%x)\n", status);
+
+    memset(buf, 0, sizeof(buf));
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 8;
+    dw = 0;
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 16, &dw);
+    ok(status == STATUS_SUCCESS, "Wrong status %x\n", status);
+    ok(dw == 16, "wrong dw value %d\n", dw);
+    ok(compare_ldt(&pli->ldt[0], &le), "Wrong LE value\n");
+    todo_wine ok(pli->dwSize == (entry + 2) * 8, "wrong pli->dwSize=%d\n", pli->dwSize);
+
+    memset(buf, 0, sizeof(buf));
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 16;
+    dw = 0;
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 24, &dw);
+    ok(status == STATUS_SUCCESS, "Wrong status %x\n", status);
+    ok(dw == 24, "wrong dw value %d\n", dw);
+    ok(compare_ldt(&pli->ldt[0], &le), "Wrong LE value\n");
+    ok(compare_ldt(&pli->ldt[1], &le), "Wrong LE value\n");
+    todo_wine ok(pli->dwSize == (entry + 2) * 8, "wrong pli->dwSize=%d\n", pli->dwSize);
+
+    memset(buf, 0, sizeof(buf));
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 12;
+    dw = 0;
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 24, &dw);
+    ok(status == STATUS_INVALID_LDT_SIZE, "Wrong status %x\n", status);
+    ok(pli->dwSize == 12, "wrong pli->dwSize=%d\n", pli->dwSize);
+
+    memset(buf, 0, sizeof(buf));
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 16;
+    status = pNtSetInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 24);
+    ok(status == STATUS_SUCCESS, "should have set LDT (%x)\n", status);
+    ok(!read_mem(LDT(entry, 3), 0, &ret), "ldt shouldn't be readable\n");
+    ok(!read_mem(LDT(entry + 1, 3), 0, &ret), "ldt shouldn't be readable\n");
+    todo_wine ok(pli->dwSize == 16, "wrong pli->dwSize=%x\n", pli->dwSize);
+
+    /* we still have the two entries defined
+     * (and Query returns all the slots ??)
+     */
+    memset(buf, 0, sizeof(buf));
+    pli->dwSelector = PROC_LDT(entry);
+    pli->dwSize = 16;
+    dw = 0;
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessLdtInformation, buf, 24, &dw);
+    ok(status == STATUS_SUCCESS, "Wrong status %x\n", status);
+    ok(dw == 24, "wrong dw value %d\n", dw);
+    todo_wine ok(pli->dwSize == (entry + 2)*8, "wrong pli->dwSize=%d\n", pli->dwSize);
+    ok(compare_ldt(&null_entry, &pli->ldt[0]), "pli->ldt[0] should be a null entry\n");
+    ok(compare_ldt(&null_entry, &pli->ldt[1]), "pli->ldt[1] should be a null entry\n");
+}
 #endif /* __i386__ */
 
 START_TEST(selector)
@@ -154,11 +384,13 @@ #ifdef __i386__
     if (!h) return;
 #define X(f) p##f = (void*)GetProcAddress(h, #f); if (!p##f) {printf("Cannot load " #f "\n"); return;}
     X(NtQueryInformationProcess);
+    X(NtSetInformationProcess);
 #undef X
     /* first, find an unused selector */
     entry = find_unused_ldt_entry();
     if (entry >= 8192) return;
 
     test_fail_process(entry);
+    test_new_ldt(entry);
 #endif /* __i386__ */
 }
diff --git a/include/wine/library.h b/include/wine/library.h
index cbaa026..21d05cf 100644
--- a/include/wine/library.h
+++ b/include/wine/library.h
@@ -121,6 +121,7 @@ #define WINE_LDT_FLAGS_DATA      0x13  /
 #define WINE_LDT_FLAGS_STACK     0x17  /* Stack segment */
 #define WINE_LDT_FLAGS_CODE      0x1b  /* Code segment */
 #define WINE_LDT_FLAGS_TYPE_MASK 0x1f  /* Mask for segment type */
+#define WINE_LDT_FLAGS_ABSENT    0x20  /* Segment is not present */
 #define WINE_LDT_FLAGS_32BIT     0x40  /* Segment is 32-bit (code or stack) */
 #define WINE_LDT_FLAGS_ALLOCATED 0x80  /* Segment is allocated (no longer free) */
 
@@ -152,7 +153,7 @@ inline static unsigned int wine_ldt_get_
 inline static void wine_ldt_set_flags( LDT_ENTRY *ent, unsigned char flags )
 {
     ent->HighWord.Bits.Dpl         = 3;
-    ent->HighWord.Bits.Pres        = 1;
+    ent->HighWord.Bits.Pres        = (flags & WINE_LDT_FLAGS_ABSENT) == 0;
     ent->HighWord.Bits.Type        = flags;
     ent->HighWord.Bits.Sys         = 0;
     ent->HighWord.Bits.Reserved_0  = 0;
@@ -162,6 +163,7 @@ inline static unsigned char wine_ldt_get
 {
     unsigned char ret = ent->HighWord.Bits.Type;
     if (ent->HighWord.Bits.Default_Big) ret |= WINE_LDT_FLAGS_32BIT;
+    if (!ent->HighWord.Bits.Pres)       ret |= WINE_LDT_FLAGS_ABSENT;
     return ret;
 }
 inline static int wine_ldt_is_empty( const LDT_ENTRY *ent )
diff --git a/libs/wine/ldt.c b/libs/wine/ldt.c
index ceb8db4..0646175 100644
--- a/libs/wine/ldt.c
+++ b/libs/wine/ldt.c
@@ -243,9 +243,8 @@ #endif  /* __i386__ */
     {
         wine_ldt_copy.base[index]  = wine_ldt_get_base(entry);
         wine_ldt_copy.limit[index] = wine_ldt_get_limit(entry);
-        wine_ldt_copy.flags[index] = (entry->HighWord.Bits.Type |
-                                 (entry->HighWord.Bits.Default_Big ? WINE_LDT_FLAGS_32BIT : 0) |
-                                 (wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED));
+        wine_ldt_copy.flags[index] &= WINE_LDT_FLAGS_ALLOCATED;
+        wine_ldt_copy.flags[index] |= wine_ldt_get_flags(entry);
     }
     return ret;
 }



More information about the wine-patches mailing list