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 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 75 RegisterTelemetryProvider( 76 VOID 77 ) 78 { 79 TraceLoggingRegister(g_TelemetryProvider); 80 } 81 82 VOID 83 UnregisterTelemetryProvider( 84 VOID 85 ) 86 { 87 TraceLoggingUnregister(g_TelemetryProvider); 88 } 89 90 VOID 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 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 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 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 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 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 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 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 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_ 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