[NTOS:KD] Protect against invalid user arguments in KdpPrompt. CORE-14057
[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 PLDR_DATA_TABLE_ENTRY LdrEntry;
157
158 /* Load symbols. Currently implemented only for KDBG! */
159 if(KdbpSymFindModule(((PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2])->BaseOfDll, NULL, -1, &LdrEntry))
160 KdbSymProcessSymbols(LdrEntry);
161 }
162 else if (ExceptionCommand == BREAKPOINT_PROMPT)
163 {
164 ULONG ReturnValue;
165 LPSTR OutString;
166 USHORT OutStringLength;
167
168 /* Get the response string and length */
169 OutString = (LPSTR)Context->Ebx;
170 OutStringLength = (USHORT)Context->Edi;
171
172 /* Call KDBG */
173 ReturnValue = KdpPrompt((LPSTR)ExceptionRecord->
174 ExceptionInformation[1],
175 (USHORT)ExceptionRecord->
176 ExceptionInformation[2],
177 OutString,
178 OutStringLength,
179 PreviousMode);
180
181 /* Return the number of characters that we received */
182 Context->Eax = ReturnValue;
183 }
184 #endif
185
186 /* This we can handle: simply bump the Program Counter */
187 KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE);
188 return TRUE;
189 }
190
191 #ifdef KDBG
192 /* Check if this is an assertion failure */
193 if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE)
194 {
195 /* Bump EIP to the instruction following the int 2C */
196 Context->Eip += 2;
197 }
198 #endif
199
200 /* Get out of here if the Debugger isn't connected */
201 if (KdDebuggerNotPresent) return FALSE;
202
203 #ifdef KDBG
204 /* Call KDBG if available */
205 Return = KdbEnterDebuggerException(ExceptionRecord,
206 PreviousMode,
207 Context,
208 TrapFrame,
209 !SecondChance);
210 #else /* not KDBG */
211 if (WrapperInitRoutine)
212 {
213 /* Call GDB */
214 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
215 Context,
216 TrapFrame);
217 }
218 #endif /* not KDBG */
219
220 /* Debugger didn't handle it, please handle! */
221 if (Return == kdHandleException) return FALSE;
222
223 /* Debugger handled it */
224 return TRUE;
225 }
226
227 BOOLEAN
228 NTAPI
229 KdpCallGdb(IN PKTRAP_FRAME TrapFrame,
230 IN PEXCEPTION_RECORD ExceptionRecord,
231 IN PCONTEXT Context)
232 {
233 KD_CONTINUE_TYPE Return = kdDoNotHandleException;
234
235 /* Get out of here if the Debugger isn't connected */
236 if (KdDebuggerNotPresent) return FALSE;
237
238 /* FIXME:
239 * Right now, the GDB wrapper seems to handle exceptions differntly
240 * from KDGB and both are called at different times, while the GDB
241 * one is only called once and that's it. I don't really have the knowledge
242 * to fix the GDB stub, so until then, we'll be using this hack
243 */
244 if (WrapperInitRoutine)
245 {
246 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
247 Context,
248 TrapFrame);
249 }
250
251 /* Debugger didn't handle it, please handle! */
252 if (Return == kdHandleException) return FALSE;
253
254 /* Debugger handled it */
255 return TRUE;
256 }
257
258 BOOLEAN
259 NTAPI
260 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
261 IN PCONTEXT Context,
262 IN KPROCESSOR_MODE PreviousMode)
263 {
264 /* KDBG has its own mechanism for ignoring user mode exceptions */
265 return FALSE;
266 }
267
268 /* PUBLIC FUNCTIONS *********************************************************/
269
270 /*
271 * @implemented
272 */
273 BOOLEAN
274 NTAPI
275 KdRefreshDebuggerNotPresent(VOID)
276 {
277 UNIMPLEMENTED;
278
279 /* Just return whatever was set previously -- FIXME! */
280 return KdDebuggerNotPresent;
281 }
282
283 /*
284 * @implemented
285 */
286 NTSTATUS
287 NTAPI
288 KdDisableDebugger(VOID)
289 {
290 KIRQL OldIrql;
291
292 /* Raise IRQL */
293 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
294
295 /* TODO: Disable any breakpoints */
296
297 /* Disable the Debugger */
298 KdDebuggerEnabled = FALSE;
299 SharedUserData->KdDebuggerEnabled = FALSE;
300
301 /* Lower the IRQL */
302 KeLowerIrql(OldIrql);
303
304 /* Return success */
305 return STATUS_SUCCESS;
306 }
307
308 /*
309 * @implemented
310 */
311 NTSTATUS
312 NTAPI
313 KdEnableDebugger(VOID)
314 {
315 KIRQL OldIrql;
316
317 /* Raise IRQL */
318 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
319
320 /* TODO: Re-enable any breakpoints */
321
322 /* Enable the Debugger */
323 KdDebuggerEnabled = TRUE;
324 SharedUserData->KdDebuggerEnabled = TRUE;
325
326 /* Lower the IRQL */
327 KeLowerIrql(OldIrql);
328
329 /* Return success */
330 return STATUS_SUCCESS;
331 }
332
333 /*
334 * @implemented
335 */
336 BOOLEAN
337 NTAPI
338 KdPollBreakIn(VOID)
339 {
340 return KdpBreakPending;
341 }
342
343 /*
344 * @unimplemented
345 */
346 NTSTATUS
347 NTAPI
348 KdPowerTransition(ULONG PowerState)
349 {
350 UNIMPLEMENTED;
351 return STATUS_NOT_IMPLEMENTED;
352 }
353
354 /*
355 * @unimplemented
356 */
357 NTSTATUS
358 NTAPI
359 KdChangeOption(IN KD_OPTION Option,
360 IN ULONG InBufferLength OPTIONAL,
361 IN PVOID InBuffer,
362 IN ULONG OutBufferLength OPTIONAL,
363 OUT PVOID OutBuffer,
364 OUT PULONG OutBufferRequiredLength OPTIONAL)
365 {
366 UNIMPLEMENTED;
367 return STATUS_NOT_IMPLEMENTED;
368 }
369
370
371 NTSTATUS
372 NTAPI
373 NtQueryDebugFilterState(IN ULONG ComponentId,
374 IN ULONG Level)
375 {
376 ULONG i;
377
378 /* Convert Level to mask if it isn't already one */
379 if (Level < 32)
380 Level = 1 << Level;
381
382 /* Check if it is not the default component */
383 if (ComponentId != MAXULONG)
384 {
385 /* No, search for an existing entry in the table */
386 for (i = 0; i < KdComponentTableEntries; i++)
387 {
388 /* Check if it is the right component */
389 if (ComponentId == KdComponentTable[i].ComponentId)
390 {
391 /* Check if mask are matching */
392 return (Level & KdComponentTable[i].Level) ? TRUE : FALSE;
393 }
394 }
395 }
396
397 /* Entry not found in the table, use default mask */
398 return (Level & Kd_DEFAULT_MASK) ? TRUE : FALSE;
399 }
400
401 NTSTATUS
402 NTAPI
403 NtSetDebugFilterState(IN ULONG ComponentId,
404 IN ULONG Level,
405 IN BOOLEAN State)
406 {
407 ULONG i;
408
409 /* Convert Level to mask if it isn't already one */
410 if (Level < 32)
411 Level = 1 << Level;
412 Level &= ~DPFLTR_MASK;
413
414 /* Check if it is the default component */
415 if (ComponentId == MAXULONG)
416 {
417 /* Yes, modify the default mask */
418 if (State)
419 Kd_DEFAULT_MASK |= Level;
420 else
421 Kd_DEFAULT_MASK &= ~Level;
422
423 return STATUS_SUCCESS;
424 }
425
426 /* Search for an existing entry */
427 for (i = 0; i < KdComponentTableEntries; i++ )
428 {
429 if (ComponentId == KdComponentTable[i].ComponentId)
430 break;
431 }
432
433 /* Check if we have found an existing entry */
434 if (i == KdComponentTableEntries)
435 {
436 /* Check if we have enough space in the table */
437 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
438 return STATUS_INVALID_PARAMETER_1;
439
440 /* Add a new entry */
441 ++KdComponentTableEntries;
442 KdComponentTable[i].ComponentId = ComponentId;
443 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
444 }
445
446 /* Update entry table */
447 if (State)
448 KdComponentTable[i].Level |= Level;
449 else
450 KdComponentTable[i].Level &= ~Level;
451
452 return STATUS_SUCCESS;
453 }
454
455 /*
456 * @unimplemented
457 */
458 NTSTATUS
459 NTAPI
460 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
461 IN PVOID InputBuffer,
462 IN ULONG InputBufferLength,
463 OUT PVOID OutputBuffer,
464 IN ULONG OutputBufferLength,
465 IN OUT PULONG ReturnLength,
466 IN KPROCESSOR_MODE PreviousMode)
467 {
468 /* HACK */
469 return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength);
470 }
471
472 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
473
474 /* EOF */