[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