[NTOS:KD] Protect against invalid user arguments for BREAKPOINT_LOAD_SYMBOLS. CORE...
[reactos.git] / ntoskrnl / kd / kdmain.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/kd/kdmain.c
5 * PURPOSE: Kernel Debugger Initialization
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <debug.h>
13
14 /* VARIABLES ***************************************************************/
15
16 BOOLEAN KdDebuggerEnabled = FALSE;
17 BOOLEAN KdEnteredDebugger = FALSE;
18 BOOLEAN KdDebuggerNotPresent = TRUE;
19 BOOLEAN KdBreakAfterSymbolLoad = FALSE;
20 BOOLEAN KdpBreakPending = FALSE;
21 BOOLEAN KdPitchDebugger = TRUE;
22 BOOLEAN KdIgnoreUmExceptions = FALSE;
23 KD_CONTEXT KdpContext;
24 ULONG Kd_WIN2000_Mask;
25 VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads);
26
27 typedef struct
28 {
29 ULONG ComponentId;
30 ULONG Level;
31 } KD_COMPONENT_DATA;
32 #define MAX_KD_COMPONENT_TABLE_ENTRIES 128
33 KD_COMPONENT_DATA KdComponentTable[MAX_KD_COMPONENT_TABLE_ENTRIES];
34 ULONG KdComponentTableEntries = 0;
35
36 ULONG Kd_DEFAULT_MASK = 1 << DPFLTR_ERROR_LEVEL;
37
38 /* PRIVATE FUNCTIONS *********************************************************/
39
40 ULONG
41 NTAPI
42 KdpServiceDispatcher(ULONG Service,
43 PVOID Buffer1,
44 ULONG Buffer1Length)
45 {
46 ULONG Result = 0;
47
48 switch (Service)
49 {
50 case BREAKPOINT_PRINT: /* DbgPrint */
51 Result = KdpPrintString(Buffer1, Buffer1Length);
52 break;
53
54 #if DBG
55 case ' soR': /* ROS-INTERNAL */
56 {
57 switch ((ULONG_PTR)Buffer1)
58 {
59 case DumpAllThreads:
60 PspDumpThreads(TRUE);
61 break;
62
63 case DumpUserThreads:
64 PspDumpThreads(FALSE);
65 break;
66
67 case KdSpare3:
68 MmDumpArmPfnDatabase(FALSE);
69 break;
70
71 default:
72 break;
73 }
74 break;
75 }
76
77 #if defined(_M_IX86) && !defined(_WINKD_) // See ke/i386/traphdlr.c
78 /* Register a debug callback */
79 case 'CsoR':
80 {
81 switch (Buffer1Length)
82 {
83 case ID_Win32PreServiceHook:
84 KeWin32PreServiceHook = Buffer1;
85 break;
86
87 case ID_Win32PostServiceHook:
88 KeWin32PostServiceHook = Buffer1;
89 break;
90
91 }
92 break;
93 }
94 #endif
95
96 /* Special case for stack frame dumps */
97 case 'DsoR':
98 {
99 KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length);
100 break;
101 }
102
103 #if defined(KDBG)
104 /* Register KDBG CLI callback */
105 case 'RbdK':
106 {
107 Result = KdbRegisterCliCallback(Buffer1, Buffer1Length);
108 break;
109 }
110 #endif /* KDBG */
111 #endif /* DBG */
112 default:
113 DPRINT1("Invalid debug service call!\n");
114 HalDisplayString("Invalid debug service call!\r\n");
115 break;
116 }
117
118 return Result;
119 }
120
121 BOOLEAN
122 NTAPI
123 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame,
124 IN PKEXCEPTION_FRAME ExceptionFrame,
125 IN PEXCEPTION_RECORD ExceptionRecord,
126 IN PCONTEXT Context,
127 IN KPROCESSOR_MODE PreviousMode,
128 IN BOOLEAN SecondChance)
129 {
130 KD_CONTINUE_TYPE Return = kdHandleException;
131 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
132
133 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
134 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
135 (ExceptionRecord->NumberParameters > 0) &&
136 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
137 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
138 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
139 (ExceptionCommand == BREAKPOINT_PRINT) ||
140 (ExceptionCommand == BREAKPOINT_PROMPT)))
141 {
142 /* Check if this is a debug print */
143 if (ExceptionCommand == BREAKPOINT_PRINT)
144 {
145 /* Print the string */
146 KdpServiceDispatcher(BREAKPOINT_PRINT,
147 (PVOID)ExceptionRecord->ExceptionInformation[1],
148 ExceptionRecord->ExceptionInformation[2]);
149
150 /* Return success */
151 KeSetContextReturnRegister(Context, STATUS_SUCCESS);
152 }
153 #ifdef KDBG
154 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS)
155 {
156 PKD_SYMBOLS_INFO SymbolsInfo;
157 KD_SYMBOLS_INFO CapturedSymbolsInfo;
158 PLDR_DATA_TABLE_ENTRY LdrEntry;
159
160 SymbolsInfo = (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2];
161 if (PreviousMode != KernelMode)
162 {
163 _SEH2_TRY
164 {
165 ProbeForRead(SymbolsInfo,
166 sizeof(*SymbolsInfo),
167 1);
168 RtlCopyMemory(&CapturedSymbolsInfo,
169 SymbolsInfo,
170 sizeof(*SymbolsInfo));
171 SymbolsInfo = &CapturedSymbolsInfo;
172 }
173 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
174 {
175 SymbolsInfo = NULL;
176 }
177 _SEH2_END;
178 }
179
180 if (SymbolsInfo != NULL)
181 {
182 /* Load symbols. Currently implemented only for KDBG! */
183 if (KdbpSymFindModule(SymbolsInfo->BaseOfDll, NULL, -1, &LdrEntry))
184 {
185 KdbSymProcessSymbols(LdrEntry);
186 }
187 }
188 }
189 else if (ExceptionCommand == BREAKPOINT_PROMPT)
190 {
191 ULONG ReturnValue;
192 LPSTR OutString;
193 USHORT OutStringLength;
194
195 /* Get the response string and length */
196 OutString = (LPSTR)Context->Ebx;
197 OutStringLength = (USHORT)Context->Edi;
198
199 /* Call KDBG */
200 ReturnValue = KdpPrompt((LPSTR)ExceptionRecord->
201 ExceptionInformation[1],
202 (USHORT)ExceptionRecord->
203 ExceptionInformation[2],
204 OutString,
205 OutStringLength,
206 PreviousMode);
207
208 /* Return the number of characters that we received */
209 Context->Eax = ReturnValue;
210 }
211 #endif
212
213 /* This we can handle: simply bump the Program Counter */
214 KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE);
215 return TRUE;
216 }
217
218 #ifdef KDBG
219 /* Check if this is an assertion failure */
220 if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE)
221 {
222 /* Bump EIP to the instruction following the int 2C */
223 Context->Eip += 2;
224 }
225 #endif
226
227 /* Get out of here if the Debugger isn't connected */
228 if (KdDebuggerNotPresent) return FALSE;
229
230 #ifdef KDBG
231 /* Call KDBG if available */
232 Return = KdbEnterDebuggerException(ExceptionRecord,
233 PreviousMode,
234 Context,
235 TrapFrame,
236 !SecondChance);
237 #else /* not KDBG */
238 if (WrapperInitRoutine)
239 {
240 /* Call GDB */
241 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
242 Context,
243 TrapFrame);
244 }
245 #endif /* not KDBG */
246
247 /* Debugger didn't handle it, please handle! */
248 if (Return == kdHandleException) return FALSE;
249
250 /* Debugger handled it */
251 return TRUE;
252 }
253
254 BOOLEAN
255 NTAPI
256 KdpCallGdb(IN PKTRAP_FRAME TrapFrame,
257 IN PEXCEPTION_RECORD ExceptionRecord,
258 IN PCONTEXT Context)
259 {
260 KD_CONTINUE_TYPE Return = kdDoNotHandleException;
261
262 /* Get out of here if the Debugger isn't connected */
263 if (KdDebuggerNotPresent) return FALSE;
264
265 /* FIXME:
266 * Right now, the GDB wrapper seems to handle exceptions differntly
267 * from KDGB and both are called at different times, while the GDB
268 * one is only called once and that's it. I don't really have the knowledge
269 * to fix the GDB stub, so until then, we'll be using this hack
270 */
271 if (WrapperInitRoutine)
272 {
273 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
274 Context,
275 TrapFrame);
276 }
277
278 /* Debugger didn't handle it, please handle! */
279 if (Return == kdHandleException) return FALSE;
280
281 /* Debugger handled it */
282 return TRUE;
283 }
284
285 BOOLEAN
286 NTAPI
287 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
288 IN PCONTEXT Context,
289 IN KPROCESSOR_MODE PreviousMode)
290 {
291 /* KDBG has its own mechanism for ignoring user mode exceptions */
292 return FALSE;
293 }
294
295 /* PUBLIC FUNCTIONS *********************************************************/
296
297 /*
298 * @implemented
299 */
300 BOOLEAN
301 NTAPI
302 KdRefreshDebuggerNotPresent(VOID)
303 {
304 UNIMPLEMENTED;
305
306 /* Just return whatever was set previously -- FIXME! */
307 return KdDebuggerNotPresent;
308 }
309
310 /*
311 * @implemented
312 */
313 NTSTATUS
314 NTAPI
315 KdDisableDebugger(VOID)
316 {
317 KIRQL OldIrql;
318
319 /* Raise IRQL */
320 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
321
322 /* TODO: Disable any breakpoints */
323
324 /* Disable the Debugger */
325 KdDebuggerEnabled = FALSE;
326 SharedUserData->KdDebuggerEnabled = FALSE;
327
328 /* Lower the IRQL */
329 KeLowerIrql(OldIrql);
330
331 /* Return success */
332 return STATUS_SUCCESS;
333 }
334
335 /*
336 * @implemented
337 */
338 NTSTATUS
339 NTAPI
340 KdEnableDebugger(VOID)
341 {
342 KIRQL OldIrql;
343
344 /* Raise IRQL */
345 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
346
347 /* TODO: Re-enable any breakpoints */
348
349 /* Enable the Debugger */
350 KdDebuggerEnabled = TRUE;
351 SharedUserData->KdDebuggerEnabled = TRUE;
352
353 /* Lower the IRQL */
354 KeLowerIrql(OldIrql);
355
356 /* Return success */
357 return STATUS_SUCCESS;
358 }
359
360 /*
361 * @implemented
362 */
363 BOOLEAN
364 NTAPI
365 KdPollBreakIn(VOID)
366 {
367 return KdpBreakPending;
368 }
369
370 /*
371 * @unimplemented
372 */
373 NTSTATUS
374 NTAPI
375 KdPowerTransition(ULONG PowerState)
376 {
377 UNIMPLEMENTED;
378 return STATUS_NOT_IMPLEMENTED;
379 }
380
381 /*
382 * @unimplemented
383 */
384 NTSTATUS
385 NTAPI
386 KdChangeOption(IN KD_OPTION Option,
387 IN ULONG InBufferLength OPTIONAL,
388 IN PVOID InBuffer,
389 IN ULONG OutBufferLength OPTIONAL,
390 OUT PVOID OutBuffer,
391 OUT PULONG OutBufferRequiredLength OPTIONAL)
392 {
393 UNIMPLEMENTED;
394 return STATUS_NOT_IMPLEMENTED;
395 }
396
397
398 NTSTATUS
399 NTAPI
400 NtQueryDebugFilterState(IN ULONG ComponentId,
401 IN ULONG Level)
402 {
403 ULONG i;
404
405 /* Convert Level to mask if it isn't already one */
406 if (Level < 32)
407 Level = 1 << Level;
408
409 /* Check if it is not the default component */
410 if (ComponentId != MAXULONG)
411 {
412 /* No, search for an existing entry in the table */
413 for (i = 0; i < KdComponentTableEntries; i++)
414 {
415 /* Check if it is the right component */
416 if (ComponentId == KdComponentTable[i].ComponentId)
417 {
418 /* Check if mask are matching */
419 return (Level & KdComponentTable[i].Level) ? TRUE : FALSE;
420 }
421 }
422 }
423
424 /* Entry not found in the table, use default mask */
425 return (Level & Kd_DEFAULT_MASK) ? TRUE : FALSE;
426 }
427
428 NTSTATUS
429 NTAPI
430 NtSetDebugFilterState(IN ULONG ComponentId,
431 IN ULONG Level,
432 IN BOOLEAN State)
433 {
434 ULONG i;
435
436 /* Convert Level to mask if it isn't already one */
437 if (Level < 32)
438 Level = 1 << Level;
439 Level &= ~DPFLTR_MASK;
440
441 /* Check if it is the default component */
442 if (ComponentId == MAXULONG)
443 {
444 /* Yes, modify the default mask */
445 if (State)
446 Kd_DEFAULT_MASK |= Level;
447 else
448 Kd_DEFAULT_MASK &= ~Level;
449
450 return STATUS_SUCCESS;
451 }
452
453 /* Search for an existing entry */
454 for (i = 0; i < KdComponentTableEntries; i++ )
455 {
456 if (ComponentId == KdComponentTable[i].ComponentId)
457 break;
458 }
459
460 /* Check if we have found an existing entry */
461 if (i == KdComponentTableEntries)
462 {
463 /* Check if we have enough space in the table */
464 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
465 return STATUS_INVALID_PARAMETER_1;
466
467 /* Add a new entry */
468 ++KdComponentTableEntries;
469 KdComponentTable[i].ComponentId = ComponentId;
470 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
471 }
472
473 /* Update entry table */
474 if (State)
475 KdComponentTable[i].Level |= Level;
476 else
477 KdComponentTable[i].Level &= ~Level;
478
479 return STATUS_SUCCESS;
480 }
481
482 /*
483 * @unimplemented
484 */
485 NTSTATUS
486 NTAPI
487 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
488 IN PVOID InputBuffer,
489 IN ULONG InputBufferLength,
490 OUT PVOID OutputBuffer,
491 IN ULONG OutputBufferLength,
492 IN OUT PULONG ReturnLength,
493 IN KPROCESSOR_MODE PreviousMode)
494 {
495 /* HACK */
496 return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength);
497 }
498
499 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
500
501 /* EOF */