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(&currentTime);
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                                 &parametersPath,
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                                &parametersPart,
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(&currentTime);
441 
442     status = Mx::MxSetValueKey(hWdf.m_Key,
443                            &wdfTimeOfLastTelemetryLog,
444                            0,
445                            REG_QWORD,
446                            &currentTime.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