Fix a buffer overflow in OutputDebugStringA. Spotted by mbealby@gmail.com.
[reactos.git] / reactos / lib / kernel32 / debug / output.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/debug/debugger.c
6 * PURPOSE: OutputDebugString()
7 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <k32.h>
13
14 /* FUNCTIONS *****************************************************************/
15
16 /* Open or create the mutex used to communicate with the debug monitor */
17 HANDLE K32CreateDBMonMutex(void)
18 {
19 static SID_IDENTIFIER_AUTHORITY siaNTAuth = {SECURITY_NT_AUTHORITY};
20 static SID_IDENTIFIER_AUTHORITY siaWorldAuth = {SECURITY_WORLD_SID_AUTHORITY};
21
22 HANDLE hMutex;
23
24 /* SIDs to be used in the DACL */
25 PSID psidSystem = NULL;
26 PSID psidAdministrators = NULL;
27 PSID psidEveryone = NULL;
28
29 /* buffer for the DACL */
30 PVOID pDaclBuf = NULL;
31
32 /*
33 minimum size of the DACL: an ACL descriptor and three ACCESS_ALLOWED_ACE
34 headers. We'll add the size of SIDs when we'll know it
35 */
36 SIZE_T nDaclBufSize =
37 sizeof(ACL) +
38 (
39 sizeof(ACCESS_ALLOWED_ACE) -
40 sizeof(((ACCESS_ALLOWED_ACE*)0)->SidStart)
41 ) * 3;
42
43 /* security descriptor of the mutex */
44 SECURITY_DESCRIPTOR sdMutexSecurity;
45
46 /* attributes of the mutex object we'll create */
47 SECURITY_ATTRIBUTES saMutexAttribs =
48 {
49 sizeof(saMutexAttribs),
50 &sdMutexSecurity,
51 TRUE
52 };
53
54 NTSTATUS nErrCode;
55
56 /* first, try to open the mutex */
57 hMutex = OpenMutexW
58 (
59 SYNCHRONIZE | READ_CONTROL | MUTANT_QUERY_STATE,
60 TRUE,
61 L"DBWinMutex"
62 );
63
64 if(hMutex != NULL)
65 {
66 /* success */
67 return hMutex;
68 }
69 /* error other than the mutex not being found */
70 else if(GetLastError() != ERROR_FILE_NOT_FOUND)
71 {
72 /* failure */
73 return NULL;
74 }
75
76 /* if the mutex doesn't exist, create it */
77 #if 0 /* please uncomment when GCC supports SEH */
78 __try
79 {
80 #else
81 #define __leave goto l_Cleanup
82 #endif
83 /* first, set up the mutex security */
84 /* allocate the NT AUTHORITY\SYSTEM SID */
85 nErrCode = RtlAllocateAndInitializeSid
86 (
87 &siaNTAuth,
88 1,
89 SECURITY_LOCAL_SYSTEM_RID,
90 0,
91 0,
92 0,
93 0,
94 0,
95 0,
96 0,
97 &psidSystem
98 );
99
100 /* failure */
101 if(!NT_SUCCESS(nErrCode)) __leave;
102
103 /* allocate the BUILTIN\Administrators SID */
104 nErrCode = RtlAllocateAndInitializeSid
105 (
106 &siaNTAuth,
107 2,
108 SECURITY_BUILTIN_DOMAIN_RID,
109 DOMAIN_ALIAS_RID_ADMINS,
110 0,
111 0,
112 0,
113 0,
114 0,
115 0,
116 &psidAdministrators
117 );
118
119 /* failure */
120 if(!NT_SUCCESS(nErrCode)) __leave;
121
122 /* allocate the Everyone SID */
123 nErrCode = RtlAllocateAndInitializeSid
124 (
125 &siaWorldAuth,
126 1,
127 0,
128 0,
129 0,
130 0,
131 0,
132 0,
133 0,
134 0,
135 &psidEveryone
136 );
137
138 /* failure */
139 if(!NT_SUCCESS(nErrCode)) __leave;
140
141 /* allocate space for the SIDs too */
142 nDaclBufSize += RtlLengthSid(psidSystem);
143 nDaclBufSize += RtlLengthSid(psidAdministrators);
144 nDaclBufSize += RtlLengthSid(psidEveryone);
145
146 /* allocate the buffer for the DACL */
147 pDaclBuf = GlobalAlloc(GMEM_FIXED, nDaclBufSize);
148
149 /* failure */
150 if(pDaclBuf == NULL) __leave;
151
152 /* create the DACL */
153 nErrCode = RtlCreateAcl(pDaclBuf, nDaclBufSize, ACL_REVISION);
154
155 /* failure */
156 if(!NT_SUCCESS(nErrCode)) __leave;
157
158 /* grant the minimum required access to Everyone */
159 nErrCode = RtlAddAccessAllowedAce
160 (
161 pDaclBuf,
162 ACL_REVISION,
163 SYNCHRONIZE | READ_CONTROL | MUTANT_QUERY_STATE,
164 psidEveryone
165 );
166
167 /* failure */
168 if(!NT_SUCCESS(nErrCode)) __leave;
169
170 /* grant full access to BUILTIN\Administrators */
171 nErrCode = RtlAddAccessAllowedAce
172 (
173 pDaclBuf,
174 ACL_REVISION,
175 MUTANT_ALL_ACCESS,
176 psidAdministrators
177 );
178
179 /* failure */
180 if(!NT_SUCCESS(nErrCode)) __leave;
181
182 /* grant full access to NT AUTHORITY\SYSTEM */
183 nErrCode = RtlAddAccessAllowedAce
184 (
185 pDaclBuf,
186 ACL_REVISION,
187 MUTANT_ALL_ACCESS,
188 psidSystem
189 );
190
191 /* failure */
192 if(!NT_SUCCESS(nErrCode)) __leave;
193
194 /* create the security descriptor */
195 nErrCode = RtlCreateSecurityDescriptor
196 (
197 &sdMutexSecurity,
198 SECURITY_DESCRIPTOR_REVISION
199 );
200
201 /* failure */
202 if(!NT_SUCCESS(nErrCode)) __leave;
203
204 /* set the descriptor's DACL to the ACL we created */
205 nErrCode = RtlSetDaclSecurityDescriptor
206 (
207 &sdMutexSecurity,
208 TRUE,
209 pDaclBuf,
210 FALSE
211 );
212
213 /* failure */
214 if(!NT_SUCCESS(nErrCode)) __leave;
215
216 /* create the mutex */
217 hMutex = CreateMutexW(&saMutexAttribs, FALSE, L"DBWinMutex");
218 #if 0
219 }
220 __finally
221 {
222 #else
223 l_Cleanup:
224 #endif
225 /* free the buffers */
226 if(pDaclBuf) GlobalFree(pDaclBuf);
227 if(psidEveryone) RtlFreeSid(psidEveryone);
228 if(psidAdministrators) RtlFreeSid(psidAdministrators);
229 if(psidSystem) RtlFreeSid(psidSystem);
230 #if 0
231 }
232 #endif
233
234 return hMutex;
235 }
236
237
238 /*
239 * @implemented
240 */
241 VOID WINAPI OutputDebugStringA(LPCSTR _OutputString)
242 {
243 #if 0
244 /* FIXME: this will be pointless until GCC does SEH */
245 __try
246 {
247 ULONG_PTR a_nArgs[2];
248
249 a_nArgs[0] = (ULONG_PTR)(strlen(_OutputString) + 1);
250 a_nArgs[1] = (ULONG_PTR)_OutputString;
251
252 /* send the string to the user-mode debugger */
253 RaiseException(0x40010006, 0, 2, a_nArgs);
254 }
255 __except(EXCEPTION_EXECUTE_HANDLER)
256 {
257 #endif
258 /*
259 no user-mode debugger: try the systemwide debug message monitor, or the
260 kernel debugger as a last resort
261 */
262
263 /* mutex used to synchronize invocations of OutputDebugString */
264 static HANDLE s_hDBMonMutex = NULL;
265 /* true if we already attempted to open/create the mutex */
266 static BOOL s_bDBMonMutexTriedOpen = FALSE;
267
268 /* local copy of the mutex handle */
269 HANDLE hDBMonMutex = s_hDBMonMutex;
270 /* handle to the Section of the shared buffer */
271 HANDLE hDBMonBuffer = NULL;
272 /*
273 pointer to the mapped view of the shared buffer. It consist of the current
274 process id followed by the message string
275 */
276 struct { DWORD ProcessId; CHAR Buffer[1]; } * pDBMonBuffer = NULL;
277 /*
278 event: signaled by the debug message monitor when OutputDebugString can write
279 to the shared buffer
280 */
281 HANDLE hDBMonBufferReady = NULL;
282 /*
283 event: to be signaled by OutputDebugString when it's done writing to the
284 shared buffer
285 */
286 HANDLE hDBMonDataReady = NULL;
287
288 /* mutex not opened, and no previous attempts to open/create it */
289 if(hDBMonMutex == NULL && !s_bDBMonMutexTriedOpen)
290 {
291 /* open/create the mutex */
292 hDBMonMutex = K32CreateDBMonMutex();
293 /* store the handle */
294 s_hDBMonMutex = hDBMonMutex;
295 }
296
297 #if 0
298 __try
299 {
300 #endif
301 /* opening the mutex failed */
302 if(hDBMonMutex == NULL)
303 {
304 /* remember next time */
305 s_bDBMonMutexTriedOpen = TRUE;
306 }
307 /* opening the mutex succeeded */
308 else
309 {
310 do
311 {
312 /* synchronize with other invocations of OutputDebugString */
313 WaitForSingleObject(hDBMonMutex, INFINITE);
314
315 /* buffer of the system-wide debug message monitor */
316 hDBMonBuffer = OpenFileMappingW(SECTION_MAP_WRITE, FALSE, L"DBWIN_BUFFER");
317
318 /* couldn't open the buffer: send the string to the kernel debugger */
319 if(hDBMonBuffer == NULL) break;
320
321 /* map the buffer */
322 pDBMonBuffer = MapViewOfFile
323 (
324 hDBMonBuffer,
325 SECTION_MAP_READ | SECTION_MAP_WRITE,
326 0,
327 0,
328 0
329 );
330
331 /* couldn't map the buffer: send the string to the kernel debugger */
332 if(pDBMonBuffer == NULL) break;
333
334 /* open the event signaling that the buffer can be accessed */
335 hDBMonBufferReady = OpenEventW(SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY");
336
337 /* couldn't open the event: send the string to the kernel debugger */
338 if(hDBMonBufferReady == NULL) break;
339
340 /* open the event to be signaled when the buffer has been filled */
341 hDBMonDataReady =
342 OpenEventW(EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY");
343 }
344 while(0);
345
346 /*
347 we couldn't connect to the system-wide debug message monitor: send the
348 string to the kernel debugger
349 */
350 if(hDBMonDataReady == NULL) ReleaseMutex(hDBMonMutex);
351 }
352
353 #if 0
354 __try
355 #else
356 do
357 #endif
358 {
359 /* size of the current output block */
360 SIZE_T nRoundLen;
361 /* size of the remainder of the string */
362 SIZE_T nOutputStringLen;
363
364 for
365 (
366 /* output the whole string */
367 nOutputStringLen = strlen(_OutputString);
368 /* repeat until the string has been fully output */
369 nOutputStringLen > 0;
370 /* move to the next block */
371 _OutputString += nRoundLen, nOutputStringLen -= nRoundLen
372 )
373 {
374 /*
375 we're connected to the debug monitor: write the current block to the
376 shared buffer
377 */
378 if(hDBMonDataReady)
379 {
380 /*
381 wait a maximum of 10 seconds for the debug monitor to finish processing
382 the shared buffer
383 */
384 if(WaitForSingleObject(hDBMonBufferReady, 10000) != WAIT_OBJECT_0)
385 {
386 /* timeout or failure: give up */
387 break;
388 }
389
390 /* write the process id into the buffer */
391 pDBMonBuffer->ProcessId = GetCurrentProcessId();
392
393 /* write only as many bytes as they fit in the buffer */
394 if(nOutputStringLen > (PAGE_SIZE - sizeof(DWORD) - 1))
395 nRoundLen = PAGE_SIZE - sizeof(DWORD) - 1;
396 else
397 nRoundLen = nOutputStringLen;
398
399 /* copy the current block into the buffer */
400 memcpy(pDBMonBuffer->Buffer, _OutputString, nRoundLen);
401
402 /* null-terminate the current block */
403 pDBMonBuffer->Buffer[nRoundLen] = 0;
404
405 /* signal that the data contains meaningful data and can be read */
406 SetEvent(hDBMonDataReady);
407 }
408 /* else, send the current block to the kernel debugger */
409 else
410 {
411 /* output in blocks of 512 characters */
412 CHAR a_cBuffer[512];
413
414 /* write a maximum of 511 bytes */
415 if(nOutputStringLen > (sizeof(a_cBuffer) - 1))
416 nRoundLen = sizeof(a_cBuffer) - 1;
417 else
418 nRoundLen = nOutputStringLen;
419
420 /* copy the current block */
421 memcpy(a_cBuffer, _OutputString, nRoundLen);
422
423 /* null-terminate the current block */
424 a_cBuffer[nRoundLen] = 0;
425
426 /* send the current block to the kernel debugger */
427 DbgPrint("%s", a_cBuffer);
428 }
429 }
430 }
431 #if 0
432 /* ignore access violations and let other exceptions fall through */
433 __except
434 (
435 (GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
436 EXCEPTION_EXECUTE_HANDLER :
437 EXCEPTION_CONTINUE_SEARCH
438 )
439 {
440 /* string copied verbatim from Microsoft's kernel32.dll */
441 DbgPrint("\nOutputDebugString faulted during output\n");
442 }
443 #else
444 while(0);
445 #endif
446
447 #if 0
448 }
449 __finally
450 {
451 #endif
452 /* close all the still open resources */
453 if(hDBMonBufferReady) CloseHandle(hDBMonBufferReady);
454 if(pDBMonBuffer) UnmapViewOfFile(pDBMonBuffer);
455 if(hDBMonBuffer) CloseHandle(hDBMonBuffer);
456 if(hDBMonDataReady) CloseHandle(hDBMonDataReady);
457
458 /* leave the critical section */
459 if(hDBMonDataReady != NULL)
460 ReleaseMutex(hDBMonMutex);
461 #if 0
462 }
463 }
464 #endif
465 }
466
467
468 /*
469 * @implemented
470 */
471 VOID WINAPI OutputDebugStringW(LPCWSTR _OutputString)
472 {
473 UNICODE_STRING wstrOut;
474 ANSI_STRING strOut;
475 NTSTATUS nErrCode;
476
477 /* convert the string in ANSI */
478 RtlInitUnicodeString(&wstrOut, _OutputString);
479 nErrCode = RtlUnicodeStringToAnsiString(&strOut, &wstrOut, TRUE);
480
481 if(!NT_SUCCESS(nErrCode))
482 {
483 /*
484 Microsoft's kernel32.dll always prints something, even in case the conversion
485 fails
486 */
487 OutputDebugStringA("");
488 }
489 else
490 {
491 /* output the converted string */
492 OutputDebugStringA(strOut.Buffer);
493
494 /* free the converted string */
495 RtlFreeAnsiString(&strOut);
496 }
497 }
498
499 /* EOF */