1 /*++
2
3 Copyright (c) Microsoft Corporation
4
5 Module Name:
6
7 FxTelemetryKm.cpp
8
9 Abstract:
10
11 This module implements a telemetry methods.
12
13 Author:
14
15
16
17 Environment:
18
19 Kernel mode only
20
21 Revision History:
22
23 Notes:
24
25 --*/
26
27 #include "fxsupportpch.hpp"
28 #include "fxldr.h"
29 #include <ntstrsafe.h>
30 #include <winmeta.h>
31 #include <telemetry\microsofttelemetry.h>
32
33 extern "C" {
34 #if defined(EVENT_TRACING)
35 #include "FxTelemetryKm.tmh"
36 #endif
37 }
38
39 /* ec044b58-3d13-3d13-936f-7b67dfb3e */
40 TRACELOGGING_DEFINE_PROVIDER(g_TelemetryProvider,
41 KMDF_FX_TRACE_LOGGING_PROVIDER_NAME,
42 (0xec044b58, 0x3d13, 0x4880, 0x93, 0x6f, 0x7b, 0x67, 0xdf, 0xb3, 0xe0, 0x56),
43 TraceLoggingOptionMicrosoftTelemetry());
44
45 VOID
AllocAndInitializeTelemetryContext(_In_ PFX_TELEMETRY_CONTEXT * TelemetryContext)46 AllocAndInitializeTelemetryContext(
47 _In_ PFX_TELEMETRY_CONTEXT* TelemetryContext
48 )
49 {
50 PFX_TELEMETRY_CONTEXT context = NULL;
51 NTSTATUS status;
52
53 context = (PFX_TELEMETRY_CONTEXT)MxMemory::MxAllocatePoolWithTag(
54 NonPagedPool,
55 sizeof(FX_TELEMETRY_CONTEXT),
56 FX_TAG);
57 if (NULL == context) {
58 goto exit;
59 }
60
61 status = ExUuidCreate(&(context->DriverSessionGUID));
62 if (!NT_SUCCESS(status)) {
63 MxMemory::MxFreePool(context);
64 context = NULL;
65 goto exit;
66 }
67
68 context->DoOnceFlagsBitmap = 0;
69
70 exit:
71 *TelemetryContext = context;
72 }
73
74 VOID
RegisterTelemetryProvider(VOID)75 RegisterTelemetryProvider(
76 VOID
77 )
78 {
79 TraceLoggingRegister(g_TelemetryProvider);
80 }
81
82 VOID
UnregisterTelemetryProvider(VOID)83 UnregisterTelemetryProvider(
84 VOID
85 )
86 {
87 TraceLoggingUnregister(g_TelemetryProvider);
88 }
89
90 VOID
LogDeviceStartTelemetryEvent(_In_ PFX_DRIVER_GLOBALS DriverGlobals,_In_opt_ FxDevice * Fdo)91 LogDeviceStartTelemetryEvent(
92 _In_ PFX_DRIVER_GLOBALS DriverGlobals,
93 _In_opt_ FxDevice* Fdo
94 )
95 {
96 //
97 // See if telemetry registered and all the criteria to log is met.
98 if (IsLoggingEnabledAndNeeded(DriverGlobals) == FALSE) {
99 return;
100 }
101
102 //
103 // Log driver info stream
104 //
105 LogDriverInfoStream(DriverGlobals, Fdo);
106 }
107
108 BOOLEAN
IsLoggingEnabledAndNeeded(_In_ PFX_DRIVER_GLOBALS DriverGlobals)109 IsLoggingEnabledAndNeeded(
110 _In_ PFX_DRIVER_GLOBALS DriverGlobals
111 )
112 {
113 LARGE_INTEGER lastLoggedTime;
114 LARGE_INTEGER currentTime;
115 LONGLONG delta;
116
117 // If provider is not enabled exit.
118 if (FALSE == FX_TELEMETRY_ENABLED(g_TelemetryProvider, DriverGlobals)) {
119 return FALSE;
120 }
121
122 ASSERT(DriverGlobals->TelemetryContext);
123
124 //
125 // If we already fired an event during PnP start we are done. This avoids
126 // repeatedly firing events during PnP rebalance.
127 //
128 if (InterlockedBitTestAndSet(
129 &DriverGlobals->TelemetryContext->DoOnceFlagsBitmap,
130 DeviceStartEventBit) != 0) {
131 return FALSE;
132 }
133
134 //
135 // log only if it has been MIN_HOURS_BEFORE_NEXT_LOG time since last log.
136 // We don't log every time driver loads to avoid sending too much data
137 // too many times in case of a buggy driver going through load/unload cycle
138 // or when a device is plugged in two many times.
139 //
140 lastLoggedTime.QuadPart = 0;
141 RegistryReadLastLoggedTime(DriverGlobals, &lastLoggedTime);
142
143 if (lastLoggedTime.QuadPart == 0) {
144 //
145 // driver is loading for first time ater install so need to log
146 // event
147 //
148 return TRUE;
149 }
150
151 Mx::MxQuerySystemTime(¤tTime);
152
153 delta = (currentTime.QuadPart - lastLoggedTime.QuadPart);
154
155 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDRIVER,
156 "lastlogged %I64x, current %I64x, delta %I64x",
157 lastLoggedTime.QuadPart, currentTime.QuadPart, delta);
158
159 //
160 // KeQuerySystemTime returns time in 100-ns. We convert MIN_HOURS_BEFORE_NEXT_LOG
161 // to 100-nano sec unit and then compare.
162 //
163 if (delta < WDF_ABS_TIMEOUT_IN_SEC(MIN_HOURS_BEFORE_NEXT_LOG * 60 * 60)) {
164 return FALSE;
165 }
166
167 return TRUE;
168 }
169
170 VOID
LogDriverInfoStream(_In_ PFX_DRIVER_GLOBALS DriverGlobals,_In_opt_ FxDevice * Fdo)171 LogDriverInfoStream(
172 _In_ PFX_DRIVER_GLOBALS DriverGlobals,
173 _In_opt_ FxDevice* Fdo
174 )
175 {
176 FxTelemetryDriverInfo driverInfo = {0};
177 FxAutoString hardwareIDs, setupClass, busEnum, manufacturer;
178
179 //
180 // Log driver and device info
181 //
182 GetDriverInfo(DriverGlobals, Fdo, &driverInfo);
183
184 if (Fdo != NULL) {
185 //
186 // Get Setup class
187 //
188 FxGetDevicePropertyString(Fdo,
189 DevicePropertyClassName,
190 &setupClass.m_UnicodeString);
191 //
192 // Get Bus enumerator
193 //
194 FxGetDevicePropertyString(Fdo,
195 DevicePropertyEnumeratorName,
196 &busEnum.m_UnicodeString);
197 //
198 // Get hardware id multi-string
199 //
200 FxGetDevicePropertyString(Fdo,
201 DevicePropertyHardwareID,
202 &hardwareIDs.m_UnicodeString);
203
204 GetFirstHardwareId(&hardwareIDs.m_UnicodeString);
205
206 //
207 // Get manufacturer
208 //
209 FxGetDevicePropertyString(Fdo,
210 DevicePropertyManufacturer,
211 &manufacturer.m_UnicodeString);
212 }
213
214 KMDF_CENSUS_EVT_WRITE_DEVICE_START(g_TelemetryProvider,
215 DriverGlobals,
216 driverInfo,
217 setupClass,
218 busEnum,
219 hardwareIDs,
220 manufacturer);
221
222 //
223 // write current time to registry
224 //
225 RegistryWriteCurrentTime(DriverGlobals);
226 }
227
228 VOID
GetFirstHardwareId(_Inout_ PUNICODE_STRING HardwareIds)229 GetFirstHardwareId(
230 _Inout_ PUNICODE_STRING HardwareIds
231 )
232 /*++
233
234 Routine Description:
235
236 This routine returns the first hardware ID present in the multi-string.
237 If the first string is longer than max allowed by Telemetry, a null value is
238 returned instead of retruning a partial ID.
239
240 Arguments:
241
242 HardwareIds - a multi-string terminated by two unicode_nulls.
243
244 --*/
245
246 {
247 PWCHAR curr;
248 USHORT lengthCch;
249
250 ASSERT(HardwareIds != NULL);
251
252 curr = (PWCHAR) HardwareIds->Buffer;
253 lengthCch = (HardwareIds->Length)/sizeof(WCHAR);
254
255 //
256 // if the caller supplied NULL buffer, then nothing to do.
257 //
258 if (curr == NULL) {
259 RtlInitUnicodeString(HardwareIds, NULL);
260 return;
261 }
262
263 //
264 // if the first element is NULL then update the length
265 //
266 if (*curr == UNICODE_NULL) {
267 HardwareIds->Length = 0;
268 HardwareIds->MaximumLength = HardwareIds->Length + sizeof(UNICODE_NULL);
269 return;
270 }
271
272 for (int i = 0; i < lengthCch; i++, curr++) {
273
274 if (*curr == UNICODE_NULL) {
275 //
276 // We found the first string. Update size. We only want to keep the
277 // first string.
278 //
279 HardwareIds->Length = (USHORT)(i * sizeof(WCHAR));
280 HardwareIds->MaximumLength = HardwareIds->Length + sizeof(UNICODE_NULL);
281 return;
282 }
283 }
284 }
285
286 VOID
GetDriverInfo(_In_ PFX_DRIVER_GLOBALS Globals,_In_opt_ FxDevice * Fdo,_Out_ FxTelemetryDriverInfo * DriverInfo)287 GetDriverInfo(
288 _In_ PFX_DRIVER_GLOBALS Globals,
289 _In_opt_ FxDevice* Fdo,
290 _Out_ FxTelemetryDriverInfo* DriverInfo
291 )
292 {
293 FxPkgPnp* pnpPkg;
294 USHORT devInfo = 0;
295
296 DriverInfo->bitmap.IsVerifierOn = Globals->FxVerifierOn;
297 DriverInfo->bitmap.IsEnhancedVerifierOn = FLAG_TO_BOOL(Globals->FxEnhancedVerifierOptions, FxEnhancedVerifierFunctionTableHookMask);
298
299 if (Fdo == NULL) {
300 //
301 // this is for non-pnp or noDispatchOverride.
302 //
303 DriverInfo->bitmap.IsNonPnpDriver = FLAG_TO_BOOL(Globals->Public.DriverFlags, WdfDriverInitNonPnpDriver);
304 DriverInfo->bitmap.IsNoDispatchOverride = FLAG_TO_BOOL(Globals->Public.DriverFlags, WdfDriverInitNoDispatchOverride);
305 }
306 else {
307 pnpPkg = Fdo->m_PkgPnp;
308 devInfo = Fdo->GetDeviceTelemetryInfoFlags();
309
310 DriverInfo->bitmap.IsFilter = Fdo->GetFdoPkg()->IsFilter();
311 DriverInfo->bitmap.IsUsingRemoveLockOption = Fdo->IsRemoveLockEnabledForIo();
312 DriverInfo->bitmap.IsUsingNonDefaultHardwareReleaseOrder = pnpPkg->IsDefaultReleaseHardwareOrder();
313 DriverInfo->bitmap.IsPowerPolicyOwner = pnpPkg->IsPowerPolicyOwner();
314 DriverInfo->bitmap.IsS0IdleWakeFromS0Enabled = pnpPkg->IsS0IdleWakeFromS0Enabled();
315 DriverInfo->bitmap.IsS0IdleUsbSSEnabled = pnpPkg->IsS0IdleUsbSSEnabled();
316 DriverInfo->bitmap.IsS0IdleSystemManaged = pnpPkg->IsS0IdleSystemManaged();
317 DriverInfo->bitmap.IsSxWakeEnabled = pnpPkg->IsSxWakeEnabled();
318 DriverInfo->bitmap.IsUsingLevelTriggeredLineInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoLineBasedLevelTriggeredInterrupt);
319 DriverInfo->bitmap.IsUsingEdgeTriggeredLineInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoLineBasedEdgeTriggeredInterrupt);
320 DriverInfo->bitmap.IsUsingMsiXOrSingleMsi22Interrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoMsiXOrSingleMsi22Interrupt);
321 DriverInfo->bitmap.IsUsingMsi22MultiMessageInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoMsi22MultiMessageInterrupt);
322 DriverInfo->bitmap.IsUsingMultipleInterrupt = pnpPkg->HasMultipleInterrupts();
323 DriverInfo->bitmap.IsUsingPassiveLevelInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoPassiveLevelInterrupt);
324 DriverInfo->bitmap.IsUsingBusMasterDma = IsDeviceInfoFlagSet(devInfo, DeviceInfoDmaBusMaster);
325 DriverInfo->bitmap.IsUsingSystemDma = IsDeviceInfoFlagSet(devInfo, DeviceInfoDmaSystem);
326 DriverInfo->bitmap.IsUsingSystemDmaDuplex = IsDeviceInfoFlagSet(devInfo, DeviceInfoDmaSystemDuplex);
327 DriverInfo->bitmap.IsUsingStaticBusEnumration = IsDeviceInfoFlagSet(devInfo, DeviceInfoHasStaticChildren);
328 DriverInfo->bitmap.IsUsingDynamicBusEnumeration = IsDeviceInfoFlagSet(devInfo, DeviceInfoHasDynamicChildren);
329 }
330 }
331
332 VOID
RegistryReadLastLoggedTime(_In_ PFX_DRIVER_GLOBALS DriverGlobals,_Out_ PLARGE_INTEGER LastLoggedTime)333 RegistryReadLastLoggedTime(
334 _In_ PFX_DRIVER_GLOBALS DriverGlobals,
335 _Out_ PLARGE_INTEGER LastLoggedTime
336 )
337 {
338 FxAutoRegKey hKey, hWdf;
339 DECLARE_CONST_UNICODE_STRING(parametersPath, L"Parameters\\Wdf");
340 DECLARE_CONST_UNICODE_STRING(valueName, WDF_LAST_TELEMETRY_LOG_TIME_VALUE);
341 LARGE_INTEGER value;
342 NTSTATUS status;
343
344 ASSERT(LastLoggedTime != NULL);
345 LastLoggedTime->QuadPart = 0;
346
347 status = FxRegKey::_OpenKey(NULL,
348 DriverGlobals->Driver->GetRegistryPathUnicodeString(),
349 &hWdf.m_Key,
350 KEY_READ);
351 if (!NT_SUCCESS(status)) {
352 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
353 "Unable to open driver's service key, status %!STATUS!", status);
354 return;
355 }
356
357 status = FxRegKey::_OpenKey(hWdf.m_Key,
358 ¶metersPath,
359 &hKey.m_Key,
360 KEY_READ);
361 if (!NT_SUCCESS(status)) {
362 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
363 "Unable to open driver's service parameters key, status %!STATUS!",
364 status);
365 return;
366 }
367
368 value.QuadPart = 0;
369 status = FxRegKey::_QueryQuadWord(
370 hKey.m_Key, &valueName, &value);
371
372 //
373 // Set value only on success.
374 //
375 if (NT_SUCCESS(status)) {
376 LastLoggedTime->QuadPart = value.QuadPart;
377 }
378 }
379
380 VOID
RegistryWriteCurrentTime(_In_ PFX_DRIVER_GLOBALS DriverGlobals)381 RegistryWriteCurrentTime(
382 _In_ PFX_DRIVER_GLOBALS DriverGlobals
383 )
384 {
385 FxAutoRegKey hDriver, hParameters, hWdf;
386 DECLARE_CONST_UNICODE_STRING(parametersPart, L"Parameters");
387 DECLARE_CONST_UNICODE_STRING(wdfPart, L"Wdf");
388 LARGE_INTEGER currentTime;
389
390 //
391 // Not defined with the macro because ZwSetValue doesn't use
392 // PCUNICODE_STRING
393 //
394 UNICODE_STRING wdfTimeOfLastTelemetryLog;
395 NTSTATUS status;
396
397 RtlInitUnicodeString(&wdfTimeOfLastTelemetryLog, WDF_LAST_TELEMETRY_LOG_TIME_VALUE);
398
399 status = FxRegKey::_OpenKey(NULL,
400 DriverGlobals->Driver->GetRegistryPathUnicodeString(),
401 &hDriver.m_Key,
402 KEY_WRITE | KEY_READ
403 );
404 if (!NT_SUCCESS(status)) {
405 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
406 "Unable to open driver's service key, status %!STATUS!", status);
407 return;
408 }
409 //
410 // Key creation, unlike user mode, must happen one level at a time, since
411 // create will also open take both steps instead of trying open first
412 //
413 status = FxRegKey::_Create(hDriver.m_Key,
414 ¶metersPart,
415 &hParameters.m_Key,
416 KEY_WRITE | KEY_READ
417 );
418 if (!NT_SUCCESS(status)) {
419 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
420 "Unable to write Parameters key, status %!STATUS!", status);
421 return;
422 }
423
424 status = FxRegKey::_Create(hParameters.m_Key,
425 &wdfPart,
426 &hWdf.m_Key,
427 KEY_WRITE | KEY_READ
428 );
429 if (!NT_SUCCESS(status)) {
430 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
431 "Unable to write Parameters key, status %!STATUS!", status);
432 return;
433 }
434
435 //
436 // Using ZwSetValueKey here to avoid having to change the implementation
437 // in FxRegKey of SetValue to a static / thiscall pair
438 //
439 currentTime.QuadPart = 0;
440 Mx::MxQuerySystemTime(¤tTime);
441
442 status = Mx::MxSetValueKey(hWdf.m_Key,
443 &wdfTimeOfLastTelemetryLog,
444 0,
445 REG_QWORD,
446 ¤tTime.QuadPart,
447 sizeof(currentTime)
448 );
449
450 if (!NT_SUCCESS(status)) {
451 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
452 "Failed to record current time for Telemetry log, status %!STATUS!",
453 status);
454 }
455 }
456
457 VOID
FxGetDevicePropertyString(_In_ FxDevice * Fdo,_In_ DEVICE_REGISTRY_PROPERTY DeviceProperty,_Out_ PUNICODE_STRING PropertyString)458 FxGetDevicePropertyString(
459 _In_ FxDevice* Fdo,
460 _In_ DEVICE_REGISTRY_PROPERTY DeviceProperty,
461 _Out_ PUNICODE_STRING PropertyString
462 )
463 {
464 MdDeviceObject pdo;
465 NTSTATUS status;
466 PFX_DRIVER_GLOBALS pFxDriverGlobals = Fdo->GetDriverGlobals();
467 ULONG length = 0;
468 PVOID buffer = NULL;
469
470 ASSERT(PropertyString != NULL);
471 RtlZeroMemory(PropertyString, sizeof(UNICODE_STRING));
472
473 pdo = Fdo->GetSafePhysicalDevice();
474 if (pdo == NULL) {
475 status = STATUS_INVALID_DEVICE_REQUEST;
476 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
477 "Could not get PDO from FDO WDFDEVICE 0x%p, %!STATUS!",
478 Fdo->GetHandle(), status);
479 return;
480 }
481
482 status = FxDevice::_GetDeviceProperty(pdo, DeviceProperty, 0, NULL, &length);
483 if (status != STATUS_BUFFER_TOO_SMALL) {
484 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
485 "Could not retrieve property %d length %d, %!STATUS!",
486 DeviceProperty, length, status);
487 return;
488 }
489
490 buffer = FxPoolAllocate(pFxDriverGlobals, PagedPool, length);
491 if (buffer == NULL) {
492 status = STATUS_INSUFFICIENT_RESOURCES;
493 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
494 "Could not allocate memory for property %d length %d, %!STATUS!",
495 DeviceProperty, length, status);
496 return;
497 }
498
499 status = FxDevice::_GetDeviceProperty(pdo, DeviceProperty, length, buffer, &length);
500 if (!NT_SUCCESS(status)) {
501 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
502 "Could not query for full buffer, size %d, for "
503 "property %d, %!STATUS!",
504 length, DeviceProperty, status);
505 FxPoolFree(buffer);
506 return;
507 }
508
509 PropertyString->Buffer = (PWCH)buffer;
510 PropertyString->Length = (USHORT) length - sizeof(UNICODE_NULL);
511 PropertyString->MaximumLength = (USHORT) length;
512
513 //
514 // ensure it's null terminated
515 //
516 PropertyString->Buffer[PropertyString->Length/sizeof(WCHAR)] = UNICODE_NULL;
517 }
518
519 _Must_inspect_result_
520 NTSTATUS
GetImageName(_In_ PFX_DRIVER_GLOBALS DriverGlobals,_Out_ PUNICODE_STRING ImageName)521 GetImageName(
522 _In_ PFX_DRIVER_GLOBALS DriverGlobals,
523 _Out_ PUNICODE_STRING ImageName
524 )
525 /*++
526
527 Routine Description:
528 Retrieve the ImageName value from the named Service registry key.
529
530 Caller is responsible for freeing the buffer allocated in ImageName::Buffer.
531
532 Arguments:
533 DriverGlobals - pointer to FX_DRIVER_GLOBALS
534
535 ImageeName - Pointer to a UNICODE_STRING which will receive the image name
536 upon a return value of NT_SUCCESS()
537
538 Return Value:
539 NTSTATUS
540
541 --*/
542 {
543 NTSTATUS status;
544 FxAutoRegKey hKey;
545 DECLARE_CONST_UNICODE_STRING(valueName, L"ImagePath");
546 UNICODE_STRING imagePath = {0};
547 UNICODE_STRING imageName = {0};
548 PKEY_VALUE_PARTIAL_INFORMATION value = NULL;
549 USHORT size;
550
551 ASSERT(ImageName != NULL);
552 RtlZeroMemory(ImageName, sizeof(UNICODE_STRING));
553
554 //
555 // Open driver's Service base key
556 //
557 status = FxRegKey::_OpenKey(NULL,
558 DriverGlobals->Driver->GetRegistryPathUnicodeString(),
559 &hKey.m_Key,
560 KEY_READ);
561 if (!NT_SUCCESS(status)) {
562 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
563 "Unable to open driver's service key, status %!STATUS!", status);
564 return status;
565 }
566
567 status = QueryAndAllocString(hKey.m_Key,
568 DriverGlobals,
569 &valueName,
570 &value);
571 if (!NT_SUCCESS(status)) {
572 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
573 "Failed to get Image name from service key, status %!STATUS!",
574 status);
575 return status;
576 }
577
578 BuildStringFromPartialInfo(value, &imagePath);
579
580 //
581 // Now read the "ImagePath" and extract just the driver filename as a new
582 // unicode string.
583 //
584 GetNameFromPath(&imagePath, &imageName);
585
586 if (imageName.Length == 0x0) {
587 status = STATUS_INVALID_PARAMETER;
588 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
589 "ERROR: GetNameFromPath could not find a name, status 0x%x\n",
590 status);
591 goto cleanUp;
592 }
593
594 //
595 // Check for interger overflow for length before we allocate memory
596 // size = path->Length + sizeof(UNICODE_NULL);
597 // len is used below to compute the string size including the NULL, so
598 // compute len to include the terminating NULL.
599 //
600 status = RtlUShortAdd(imageName.Length, sizeof(UNICODE_NULL), &size);
601 if (!NT_SUCCESS(status)) {
602 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
603 "ERROR: size computation failed with Status 0x%x\n", status);
604 goto cleanUp;
605 }
606
607 //
608 // allocate a buffer to hold Unicode string + null char.
609 //
610 ImageName->Buffer = (PWCH) FxPoolAllocate(DriverGlobals, PagedPool, size);
611
612 if (ImageName->Buffer == NULL) {
613 status = STATUS_INSUFFICIENT_RESOURCES;
614 DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
615 "ERROR: ExAllocatePoolWithTag failed with Status 0x%x\n", status);
616 goto cleanUp;
617 }
618
619 RtlZeroMemory(ImageName->Buffer, size);
620 ImageName->Length = 0x0;
621 ImageName->MaximumLength = size;
622
623 status = RtlUnicodeStringCopy(ImageName, &imageName);
624
625 //
626 // The copy cannot fail since we setup the buffer to hold enough space for
627 // the contents of the ImagePath value.
628 //
629 ASSERT(NT_SUCCESS(status));
630
631 cleanUp:
632
633 if (value != NULL) {
634 FxPoolFree(value);
635 }
636
637 return status;
638 }
639
640
641 _Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)642 __drv_maxIRQL(PASSIVE_LEVEL)
643 NTSTATUS
644 QueryAndAllocString(
645 _In_ HANDLE Key,
646 _In_ PFX_DRIVER_GLOBALS Globals,
647 _In_ PCUNICODE_STRING ValueName,
648 _Out_ PKEY_VALUE_PARTIAL_INFORMATION* Info
649 )
650 {
651 PKEY_VALUE_PARTIAL_INFORMATION info;
652 NTSTATUS status;
653 ULONG length;
654
655 status = STATUS_UNSUCCESSFUL;
656 info = NULL;
657
658 ASSERT(Info != NULL);
659 *Info = NULL;
660
661 status = Mx::MxQueryValueKey(Key,
662 (PUNICODE_STRING)ValueName,
663 KeyValuePartialInformation,
664 NULL,
665 0,
666 &length);
667
668 if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {
669 goto cleanup;
670 }
671
672 //
673 // Pool can be paged b/c we are running at PASSIVE_LEVEL and we are going
674 // to free it at the end of this function.
675 //
676 status = RtlULongAdd(length,
677 FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data),
678 &length);
679
680 if (!NT_SUCCESS(status)) {
681 goto cleanup;
682 }
683
684 info = (PKEY_VALUE_PARTIAL_INFORMATION) FxPoolAllocate(Globals,
685 PagedPool,
686 length);
687
688 if (info == NULL) {
689 status = STATUS_INSUFFICIENT_RESOURCES;
690 goto cleanup;
691 }
692
693 RtlZeroMemory(info, length);
694
695 //
696 // Query registry for the data under ValueName
697 //
698 status = Mx::MxQueryValueKey(Key,
699 (PUNICODE_STRING) ValueName,
700 KeyValuePartialInformation,
701 info,
702 length,
703 &length);
704
705
706 if (NT_SUCCESS(status)) {
707 if (info->Type != REG_SZ && info->Type != REG_EXPAND_SZ) {
708 status = STATUS_OBJECT_TYPE_MISMATCH;
709 goto cleanup;
710 }
711
712 if (info->DataLength == 0 ||
713 (info->DataLength % 2) != 0 ||
714 (info->DataLength >
715 (length - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)))) {
716 status = STATUS_INVALID_PARAMETER;
717 goto cleanup;
718 }
719
720 *Info = info;
721 }
722
723 cleanup:
724
725 if (!NT_SUCCESS(status)) {
726 if (info != NULL) {
727 FxPoolFree(info);
728 }
729 }
730
731 return status;
732 }
733