ntdll/tests: Win64 printf format warning fixes.
[wine] / dlls / ntdll / tests / exception.c
1 /*
2  * Unit test suite for ntdll exceptions
3  *
4  * Copyright 2005 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #ifndef _WIN32_WINNT
24 #define _WIN32_WINNT 0x500 /* For NTSTATUS */
25 #endif
26
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winnt.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #include "excpt.h"
35 #include "wine/test.h"
36
37 #ifdef __i386__
38
39 static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
40 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
41
42 /* Test various instruction combinations that cause a protection fault on the i386,
43  * and check what the resulting exception looks like.
44  */
45
46 static const struct exception
47 {
48     BYTE     code[18];   /* asm code */
49     BYTE     offset;     /* offset of faulting instruction */
50     BYTE     length;     /* length of faulting instruction */
51     NTSTATUS status;     /* expected status code */
52     DWORD    nb_params;  /* expected number of parameters */
53     DWORD    params[4];  /* expected parameters */
54 } exceptions[] =
55 {
56     /* test some privileged instructions */
57     { { 0xfb, 0xc3 },  /* sti; ret */
58       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
59     { { 0x6c, 0xc3 },  /* insb (%dx); ret */
60       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
61     { { 0x6d, 0xc3 },  /* insl (%dx); ret */
62       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
63     { { 0x6e, 0xc3 },  /* outsb (%dx); ret */
64       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
65     { { 0x6f, 0xc3 },  /* outsl (%dx); ret */
66       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
67     { { 0xe4, 0x11, 0xc3 },  /* inb $0x11,%al; ret */
68       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
69     { { 0xe5, 0x11, 0xc3 },  /* inl $0x11,%eax; ret */
70       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
71     { { 0xe6, 0x11, 0xc3 },  /* outb %al,$0x11; ret */
72       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
73     { { 0xe7, 0x11, 0xc3 },  /* outl %eax,$0x11; ret */
74       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
75     { { 0xed, 0xc3 },  /* inl (%dx),%eax; ret */
76       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
77     { { 0xee, 0xc3 },  /* outb %al,(%dx); ret */
78       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
79     { { 0xef, 0xc3 },  /* outl %eax,(%dx); ret */
80       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
81     { { 0xf4, 0xc3 },  /* hlt; ret */
82       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
83     { { 0xfa, 0xc3 },  /* cli; ret */
84       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
85
86     /* test long jump to invalid selector */
87     { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 },  /* ljmp $0,$0; ret */
88       0, 7, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
89
90     /* test iret to invalid selector */
91     { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
92       /* pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
93       6, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
94
95     /* test loading an invalid selector */
96     { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 },  /* mov $beef,%ax; mov %ax,%gs; ret */
97       5, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } },
98
99     /* test accessing a zero selector */
100     { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
101           /* push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es */
102       5, 6, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
103
104     /* test moving %cs -> %ss */
105     { { 0x0e, 0x17, 0x58, 0xc3 },  /* pushl %cs; popl %ss; popl %eax; ret */
106       1, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
107
108     /* test overlong instruction (limit is 16 bytes) */
109     { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
110       0, 16, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
111     { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
112       0, 15, STATUS_PRIVILEGED_INSTRUCTION, 0 },
113
114     /* test invalid interrupt */
115     { { 0xcd, 0xff, 0xc3 },   /* int $0xff; ret */
116       0, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
117
118     /* test moves to/from Crx */
119     { { 0x0f, 0x20, 0xc0, 0xc3 },  /* movl %cr0,%eax; ret */
120       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
121     { { 0x0f, 0x20, 0xe0, 0xc3 },  /* movl %cr4,%eax; ret */
122       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
123     { { 0x0f, 0x22, 0xc0, 0xc3 },  /* movl %eax,%cr0; ret */
124       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
125     { { 0x0f, 0x22, 0xe0, 0xc3 },  /* movl %eax,%cr4; ret */
126       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
127
128     /* test moves to/from Drx */
129     { { 0x0f, 0x21, 0xc0, 0xc3 },  /* movl %dr0,%eax; ret */
130       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
131     { { 0x0f, 0x21, 0xc8, 0xc3 },  /* movl %dr1,%eax; ret */
132       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
133     { { 0x0f, 0x21, 0xf8, 0xc3 },  /* movl %dr7,%eax; ret */
134       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
135     { { 0x0f, 0x23, 0xc0, 0xc3 },  /* movl %eax,%dr0; ret */
136       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
137     { { 0x0f, 0x23, 0xc8, 0xc3 },  /* movl %eax,%dr1; ret */
138       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
139     { { 0x0f, 0x23, 0xf8, 0xc3 },  /* movl %eax,%dr7; ret */
140       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
141
142     /* test memory reads */
143     { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffc,%eax; ret */
144       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
145     { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffd,%eax; ret */
146       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
147     { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffe,%eax; ret */
148       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
149     { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xffffffff,%eax; ret */
150       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
151
152     /* test memory writes */
153     { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffc; ret */
154       0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
155     { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffd; ret */
156       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
157     { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffe; ret */
158       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
159     { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xffffffff; ret */
160       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
161 };
162
163 static int got_exception;
164
165 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
166                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
167 {
168     const struct exception *except = *(const struct exception **)(frame + 1);
169     unsigned int i, entry = except - exceptions;
170
171     got_exception++;
172     trace( "exception: %x flags:%x addr:%p\n",
173            rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
174
175     ok( rec->ExceptionCode == except->status,
176         "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
177     ok( rec->ExceptionAddress == except->code + except->offset,
178         "%u: Wrong exception address %p/%p\n", entry,
179         rec->ExceptionAddress, except->code + except->offset );
180
181     ok( rec->NumberParameters == except->nb_params,
182         "%u: Wrong number of parameters %u/%u\n", entry, rec->NumberParameters, except->nb_params );
183     for (i = 0; i < rec->NumberParameters; i++)
184         ok( rec->ExceptionInformation[i] == except->params[i],
185             "%u: Wrong parameter %d: %lx/%x\n",
186             entry, i, rec->ExceptionInformation[i], except->params[i] );
187
188     /* don't handle exception if it's not the address we expected */
189     if (rec->ExceptionAddress != except->code + except->offset) return ExceptionContinueSearch;
190
191     context->Eip += except->length;
192     return ExceptionContinueExecution;
193 }
194
195 static void test_prot_fault(void)
196 {
197     unsigned int i;
198     struct
199     {
200         EXCEPTION_REGISTRATION_RECORD frame;
201         const struct exception       *except;
202     } exc_frame;
203
204     pNtCurrentTeb = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtCurrentTeb" );
205     if (!pNtCurrentTeb)
206     {
207         trace( "NtCurrentTeb not found, skipping tests\n" );
208         return;
209     }
210
211     exc_frame.frame.Handler = handler;
212     exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
213     pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
214     for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++)
215     {
216         void (*func)(void) = (void *)exceptions[i].code;
217         exc_frame.except = &exceptions[i];
218         got_exception = 0;
219         func();
220         if (!i && !got_exception)
221         {
222             trace( "No exception, assuming win9x, no point in testing further\n" );
223             break;
224         }
225         ok( got_exception == (exceptions[i].status != 0),
226             "%u: bad exception count %d\n", i, got_exception );
227     }
228     pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
229 }
230
231 static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
232                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
233 {
234     context->Eip += 2;  /* Skips the popl (%eax) */
235     context->Dr0 = 0x42424242;
236     context->Dr1 = 0;
237     context->Dr2 = 0;
238     context->Dr3 = 0;
239     context->Dr6 = 0;
240     context->Dr7 = 0x155;
241     return ExceptionContinueExecution;
242 }
243
244 static BYTE code[5] = {
245         0x31, 0xc0, /* xor    %eax,%eax */
246         0x8f, 0x00, /* popl   (%eax) - cause exception */
247         0xc3        /* ret */
248 };
249
250 static void test_debug_regs(void)
251 {
252     CONTEXT ctx;
253     NTSTATUS res;
254     struct
255     {
256         EXCEPTION_REGISTRATION_RECORD frame;
257     } exc_frame;
258     void (*func)(void) = (void*)code;
259
260     pNtCurrentTeb = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtCurrentTeb" );
261     if (!pNtCurrentTeb)
262     {
263         trace( "NtCurrentTeb not found, skipping tests\n" );
264         return;
265     }
266     pNtGetContextThread = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtGetContextThread" );
267     if (!pNtGetContextThread)
268     {
269         trace( "NtGetContextThread not found, skipping tests\n" );
270         return;
271     }
272
273     exc_frame.frame.Handler = dreg_handler;
274     exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
275     pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
276     func();
277     pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
278     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
279     res = pNtGetContextThread(GetCurrentThread(), &ctx);
280     ok (res == STATUS_SUCCESS,"NtGetContextThread failed with %x\n", res);
281     ok(ctx.Dr0 == 0x42424242,"failed to set debugregister 0 to 0x42424242, got %x\n", ctx.Dr0);
282     ok(ctx.Dr7 == 0x155,"failed to set debugregister 7 to 0x155, got %x\n", ctx.Dr7);
283 }
284
285 /* test the single step exception behaviour */
286 static int gotsinglesteps = 0;
287 static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
288                                   CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
289 {
290     gotsinglesteps++;
291     ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
292     return ExceptionContinueExecution;
293 }
294
295 static BYTE single_stepcode[] = {
296     0x9c,               /* pushf */
297     0x58,               /* pop   %eax */
298     0x0d,0,1,0,0,       /* or    $0x100,%eax */
299     0x50,               /* push   %eax */
300     0x9d,               /* popf    */
301     0x35,0,1,0,0,       /* xor    $0x100,%eax */
302     0x50,               /* push   %eax */
303     0x9d,               /* popf    */
304     0xc3
305 };
306
307 static void test_single_step(void)
308 {
309     struct {
310         EXCEPTION_REGISTRATION_RECORD frame;
311     } exc_frame;
312     void (*func)(void) = (void *)single_stepcode;
313
314     pNtCurrentTeb = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtCurrentTeb" );
315     if (!pNtCurrentTeb) {
316         trace( "NtCurrentTeb not found, skipping tests\n" );
317         return;
318     }
319     exc_frame.frame.Handler = single_step_handler;
320     exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
321     pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
322     func();
323     ok(gotsinglesteps == 1, "expected 1 single step exceptions, got %d\n", gotsinglesteps);
324     pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
325 }
326
327 /* Test the alignment check (AC) flag handling. */
328 static BYTE align_check_code[] = {
329     0x55,                       /* push   %ebp */
330     0x89,0xe5,                  /* mov    %esp,%ebp */
331     0x9c,                       /* pushf   */
332     0x58,                       /* pop    %eax */
333     0x0d,0,0,4,0,               /* or     $0x40000,%eax */
334     0x50,                       /* push   %eax */
335     0x9d,                       /* popf    */
336     0x8b,0x45,8,                /* mov    0x8(%ebp),%eax */
337     0xc7,0x40,1,42,0,0,0,       /* movl   $42,0x1(%eax) */
338     0x9c,                       /* pushf   */
339     0x58,                       /* pop    %eax */
340     0x35,0,0,4,0,               /* xor    $0x40000,%eax */
341     0x50,                       /* push   %eax */
342     0x9d,                       /* popf    */
343     0x5d,                       /* pop    %ebp */
344     0xc3,                       /* ret     */
345 };
346
347 static int gotalignfaults = 0;
348 static DWORD align_check_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
349                                   CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
350 {
351     ok (!(context->EFlags & 0x40000), "eflags has AC bit set\n");
352     gotalignfaults++;
353     return ExceptionContinueExecution;
354 }
355
356 static void test_align_faults(void)
357 {
358     int twoints[2];
359     struct {
360         EXCEPTION_REGISTRATION_RECORD frame;
361     } exc_frame;
362     void (*func1)(int*) = (void *)align_check_code;
363
364     pNtCurrentTeb = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtCurrentTeb" );
365     if (!pNtCurrentTeb) {
366         trace( "NtCurrentTeb not found, skipping tests\n" );
367         return;
368     }
369     exc_frame.frame.Handler = align_check_handler;
370     exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
371     pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
372
373     gotalignfaults=0;
374     func1(twoints);
375     ok(gotalignfaults == 0, "got %d alignment faults, expected 0\n", gotalignfaults);
376     pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
377 }
378
379 #endif  /* __i386__ */
380
381 START_TEST(exception)
382 {
383 #ifdef __i386__
384     test_prot_fault();
385     test_debug_regs();
386     test_single_step();
387     test_align_faults();
388 #endif
389 }