1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 FxWmiInstance.cpp 8 9 Abstract: 10 11 This module implements the FxWmiInstance object and its derivations 12 13 Author: 14 15 16 17 Revision History: 18 19 20 --*/ 21 22 #include "fxwmipch.hpp" 23 24 extern "C" { 25 // #include "FxWmiInstance.tmh" 26 } 27 28 FxWmiInstance::FxWmiInstance( 29 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 30 __in USHORT ObjectSize, 31 __in FxWmiProvider* Provider 32 ) : 33 FxNonPagedObject(FX_TYPE_WMI_INSTANCE, ObjectSize, FxDriverGlobals) 34 { 35 InitializeListHead(&m_ListEntry); 36 m_Provider = Provider; 37 m_Provider->ADDREF(this); 38 MarkDisposeOverride(ObjectDoNotLock); 39 } 40 41 FxWmiInstance::~FxWmiInstance() 42 { 43 ASSERT(IsListEmpty(&m_ListEntry)); 44 } 45 46 BOOLEAN 47 FxWmiInstance::Dispose( 48 VOID 49 ) 50 { 51 m_Provider->RemoveInstance(this); 52 m_Provider->RELEASE(this); 53 54 // 55 // Object is being deleted, remove this object from the provider's list 56 // of instances. If we don't do this, the provider will have a list which 57 // contains entries which have been freed. 58 // 59 return FxNonPagedObject::Dispose(); // __super call 60 } 61 62 _Must_inspect_result_ 63 NTSTATUS 64 FxWmiInstance::FireEvent( 65 __in_bcount_opt(EventBufferSize) PVOID EventBuffer, 66 __inout ULONG EventBufferSize 67 ) 68 { 69 ULONG sizeNeeded; 70 PWNODE_SINGLE_INSTANCE pNode; 71 NTSTATUS status; 72 73 if (EventBuffer == NULL) { 74 EventBufferSize = 0; 75 } 76 77 sizeNeeded = sizeof(WNODE_SINGLE_INSTANCE) + EventBufferSize; 78 79 // 80 // IoWMIWriteEvent will free the memory by calling ExFreePool. This means 81 // we cannot use a framework allocate function. 82 // 83 pNode = (PWNODE_SINGLE_INSTANCE) 84 ExAllocatePoolWithTag(NonPagedPool, sizeNeeded, GetDriverGlobals()->Tag); 85 86 if (pNode != NULL) { 87 RtlCopyMemory(&pNode->WnodeHeader.Guid, 88 m_Provider->GetGUID(), 89 sizeof(GUID)); 90 91 pNode->WnodeHeader.ProviderId = IoWMIDeviceObjectToProviderId( 92 GetDevice()->GetDeviceObject()); 93 pNode->WnodeHeader.BufferSize = sizeNeeded; 94 pNode->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE | 95 WNODE_FLAG_EVENT_ITEM | 96 WNODE_FLAG_STATIC_INSTANCE_NAMES; 97 KeQuerySystemTime(&pNode->WnodeHeader.TimeStamp); 98 99 pNode->InstanceIndex = m_Provider->GetInstanceIndex(this); 100 pNode->SizeDataBlock = EventBufferSize; 101 pNode->DataBlockOffset = sizeof(WNODE_SINGLE_INSTANCE); 102 103 if (EventBuffer != NULL) { 104 RtlCopyMemory(&pNode->VariableData, EventBuffer, EventBufferSize); 105 } 106 107 // 108 // Upon success, IoWMIWriteEvent will free pNode. 109 // 110 status = IoWMIWriteEvent(pNode); 111 112 if (!NT_SUCCESS(status)) { 113 ExFreePool(pNode); 114 } 115 } 116 else { 117 status = STATUS_INSUFFICIENT_RESOURCES; 118 119 DoTraceLevelMessage( 120 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE, 121 "WDFWMIINSTANCE %p insufficient resources to fire event,%!STATUS!", 122 GetHandle(), status); 123 } 124 125 return status; 126 } 127 128 FxWmiInstanceExternal::FxWmiInstanceExternal( 129 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 130 __in PWDF_WMI_INSTANCE_CONFIG Config, 131 __in FxWmiProvider* Provider 132 ) : 133 FxWmiInstance(FxDriverGlobals, sizeof(FxWmiInstanceExternal), Provider), 134 m_QueryInstanceCallback(FxDriverGlobals), 135 m_SetInstanceCallback(FxDriverGlobals), 136 m_SetItemCallback(FxDriverGlobals), 137 m_ExecuteMethodCallback(FxDriverGlobals) 138 { 139 m_ContextLength = 0; 140 m_UseContextForQuery = Config->UseContextForQuery; 141 142 if (m_UseContextForQuery == FALSE) { 143 m_QueryInstanceCallback.m_Method = Config->EvtWmiInstanceQueryInstance; 144 } 145 m_SetInstanceCallback.m_Method = Config->EvtWmiInstanceSetInstance; 146 m_SetItemCallback.m_Method = Config->EvtWmiInstanceSetItem; 147 148 m_ExecuteMethodCallback.m_Method = Config->EvtWmiInstanceExecuteMethod; 149 } 150 151 _Must_inspect_result_ 152 NTSTATUS 153 FxWmiInstanceExternal::_Create( 154 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 155 __in FxWmiProvider* Provider, 156 __in PWDF_WMI_INSTANCE_CONFIG WmiInstanceConfig, 157 __in_opt PWDF_OBJECT_ATTRIBUTES InstanceAttributes, 158 __out WDFWMIINSTANCE* WmiInstance, 159 __out FxWmiInstanceExternal** Instance 160 ) 161 { 162 FxWmiInstanceExternal* pInstance; 163 WDFWMIINSTANCE hInstance; 164 NTSTATUS status; 165 size_t contextSize; 166 167 contextSize = 0; 168 *Instance = 0; 169 170 *WmiInstance = NULL; 171 172 // 173 // For event only providers, you cannot specify any callbacks or context 174 // usage. 175 // 176 if (Provider->IsEventOnly() && 177 (WmiInstanceConfig->UseContextForQuery || 178 WmiInstanceConfig->EvtWmiInstanceQueryInstance != NULL || 179 WmiInstanceConfig->EvtWmiInstanceSetInstance != NULL || 180 WmiInstanceConfig->EvtWmiInstanceSetItem != NULL || 181 WmiInstanceConfig->EvtWmiInstanceExecuteMethod != NULL)) { 182 183 status = STATUS_INVALID_PARAMETER; 184 185 DoTraceLevelMessage( 186 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE, 187 "WDFWMIPROVIDER %p is event only and UseContextForQuery (%d) is TRUE," 188 " or a callback (query instance %p, set instance %p, set item %p, " 189 "executue method %p) is not NULL, %!STATUS!", 190 Provider->GetHandle(), WmiInstanceConfig->UseContextForQuery, 191 WmiInstanceConfig->EvtWmiInstanceQueryInstance, 192 WmiInstanceConfig->EvtWmiInstanceSetInstance, 193 WmiInstanceConfig->EvtWmiInstanceSetItem, 194 WmiInstanceConfig->EvtWmiInstanceExecuteMethod, status); 195 196 return status; 197 } 198 199 status = FxValidateObjectAttributes(FxDriverGlobals, 200 InstanceAttributes, 201 FX_VALIDATE_OPTION_PARENT_NOT_ALLOWED); 202 if (!NT_SUCCESS(status)) { 203 return status; 204 } 205 206 if (WmiInstanceConfig->UseContextForQuery) { 207 // 208 // UseContextForQuery only supported for read only instances. 209 // ExecuteMethod has undefined side affects, so we allow it. 210 // 211 if (WmiInstanceConfig->EvtWmiInstanceSetInstance != NULL || 212 WmiInstanceConfig->EvtWmiInstanceSetItem != NULL) { 213 status = STATUS_INVALID_PARAMETER; 214 215 DoTraceLevelMessage( 216 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE, 217 "UseContextForQuery set, i.e. a read only instance, but " 218 "EvtWmiInstanceSetInstance %p or EvtWmiInstanceSetItem %p is " 219 "set, %!STATUS!", 220 WmiInstanceConfig->EvtWmiInstanceSetInstance, 221 WmiInstanceConfig->EvtWmiInstanceSetItem, status); 222 223 return status; 224 } 225 226 // 227 // We must have a context to use for the query 228 // 229 if (InstanceAttributes == NULL || 230 InstanceAttributes->ContextTypeInfo == NULL) { 231 status = STATUS_INVALID_PARAMETER; 232 233 DoTraceLevelMessage( 234 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE, 235 "UseContextForQuery set, but InstanceAttributes %p is null or " 236 "there is no associated type, %!STATUS!", 237 InstanceAttributes, status); 238 239 return status; 240 } 241 242 contextSize = InstanceAttributes->ContextTypeInfo->ContextSize; 243 244 if (InstanceAttributes->ContextSizeOverride != 0) { 245 status = RtlSizeTAdd(contextSize, 246 InstanceAttributes->ContextSizeOverride, 247 &contextSize); 248 if (!NT_SUCCESS(status)) { 249 DoTraceLevelMessage( 250 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE, 251 "Overlfow adding contextSize %I64d with size override %I64d, " 252 "%!STATUS!", contextSize, 253 InstanceAttributes->ContextSizeOverride, status); 254 255 return status; 256 } 257 } 258 259 if (contextSize > ULONG_MAX) { 260 // 261 // Since we are casting to a ULONG below, detect loss of data here 262 // (only really applicable on 64 bit machines where sizeof(size_t) != 263 // sizeof(ULONG) 264 // 265 status = STATUS_INTEGER_OVERFLOW; 266 267 DoTraceLevelMessage( 268 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE, 269 "context size %I64d can be %d large, %!STATUS!", 270 contextSize, ULONG_MAX, status); 271 272 return status; 273 } 274 275 // 276 // Make sure the context is the minimum the buffer size. 277 // 278 if (contextSize < Provider->GetMinInstanceBufferSize()) { 279 status = STATUS_BUFFER_TOO_SMALL; 280 281 DoTraceLevelMessage( 282 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE, 283 "context size %I64d is less then the WDFWMIPROVIDER %p min size " 284 "of %d, %!STATUS!", 285 contextSize, Provider->GetHandle(), 286 Provider->GetMinInstanceBufferSize(), status); 287 288 return status; 289 } 290 } 291 292 pInstance = new(FxDriverGlobals, InstanceAttributes) 293 FxWmiInstanceExternal(FxDriverGlobals, WmiInstanceConfig, Provider); 294 295 if (pInstance == NULL) { 296 status = STATUS_INSUFFICIENT_RESOURCES; 297 298 DoTraceLevelMessage( 299 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE, 300 "could not allocate memory for WDFWMIINSTANCE, %!STATUS!", 301 status); 302 303 return status; 304 } 305 306 if (contextSize > 0) { 307 pInstance->SetContextForQueryLength((ULONG) contextSize); 308 } 309 310 if (NT_SUCCESS(status)) { 311 status = pInstance->Commit( 312 InstanceAttributes, (PWDFOBJECT) &hInstance, Provider); 313 314 if (NT_SUCCESS(status)) { 315 // 316 // Assign the handle back to the caller. 317 // 318 *WmiInstance = hInstance; 319 } 320 else { 321 // 322 // On failure, DeleteFromFailedCreate will delete the object and 323 // the Dispose callback will remove the instance from the provider's 324 // list. 325 // 326 DO_NOTHING(); 327 } 328 } 329 330 if (NT_SUCCESS(status)) { 331 *Instance = pInstance; 332 } 333 else { 334 pInstance->DeleteFromFailedCreate(); 335 } 336 337 return status; 338 } 339 340 BOOLEAN 341 FxWmiInstanceExternal::IsQueryInstanceSupported( 342 VOID 343 ) 344 { 345 // 346 // If we have a function pointer to call or we are using the context 347 // as the buffer, query instance is supported. 348 // 349 // Also, if neither of the first 2 are true, we need to support query 350 // instance if the device has an execute method callback b/c WMI will 351 // send a query instance to this instance which much succeed for the 352 // execute method irp to be sent. 353 // 354 return (m_UseContextForQuery || 355 m_QueryInstanceCallback.m_Method != NULL || 356 m_ExecuteMethodCallback.m_Method != NULL) ? TRUE 357 : FALSE; 358 } 359 360 _Must_inspect_result_ 361 __drv_sameIRQL 362 __drv_maxIRQL(PASSIVE_LEVEL) 363 NTSTATUS 364 FxWmiInstanceExternal::QueryInstance( 365 __inout ULONG OutBufferSize, 366 __out_bcount(OutBufferSize) PVOID OutBuffer, 367 __out PULONG BufferUsed 368 ) 369 { 370 NTSTATUS status; 371 372 if (m_UseContextForQuery) { 373 // 374 // No matter what, we are reporting the length of the context. If the 375 // buffer is too small, it is used to report the desired buffer length. 376 // Otherwise, it is the amount of data we copied to the query buffer. 377 // 378 *BufferUsed = m_ContextLength; 379 380 if (OutBufferSize < m_ContextLength) { 381 status = STATUS_BUFFER_TOO_SMALL; 382 383 DoTraceLevelMessage( 384 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE, 385 "WDFWMIINSTANCE %p query instance using context for query, " 386 "query buffer length %d, context length %d, %!STATUS!", 387 GetHandle(), OutBufferSize, m_ContextLength, status); 388 } 389 else { 390 status = STATUS_SUCCESS; 391 392 RtlCopyMemory(OutBuffer, 393 &GetContextHeader()->Context[0], 394 m_ContextLength); 395 } 396 } 397 else if (m_QueryInstanceCallback.m_Method != NULL) { 398 BYTE dummy; 399 400 if (OutBufferSize == 0) { 401 ASSERT(m_Provider->GetMinInstanceBufferSize() == 0); 402 OutBuffer = (PVOID) &dummy; 403 OutBufferSize = sizeof(dummy); 404 } 405 406 status = m_QueryInstanceCallback.Invoke( 407 GetDevice()->GetHandle(), 408 GetHandle(), 409 OutBufferSize, 410 OutBuffer, 411 BufferUsed 412 ); 413 414 if (status == STATUS_PENDING) { 415 DoTraceLevelMessage( 416 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 417 "WDFWMIINSTANCE %p was queried and returned %!STATUS!, which is " 418 "not an allowed return value", GetHandle(), status); 419 420 FxVerifierDbgBreakPoint(GetDriverGlobals()); 421 422 status = STATUS_UNSUCCESSFUL; 423 *BufferUsed = 0; 424 } 425 else if (NT_SUCCESS(status)) { 426 if (*BufferUsed > OutBufferSize) { 427 // 428 // Caller error, they returned more bytes in *BufferUsed then 429 // was passed in via OutBufferSize, yet returned NT_SUCCESS 430 // 431 DoTraceLevelMessage( 432 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 433 "WDFWMIINSTANCE %p was queried with buffer size %d, " 434 " but returned %d bytes and %!STATUS!, should return " 435 "!NT_SUCCESS in this case", 436 GetHandle(), OutBufferSize, *BufferUsed, status); 437 438 FxVerifierDbgBreakPoint(GetDriverGlobals()); 439 440 status = STATUS_UNSUCCESSFUL; 441 *BufferUsed = 0; 442 } 443 else if (OutBuffer == &dummy && *BufferUsed > 0) { 444 // 445 // Convert success back to an error where we can report the 446 // required size back to the caller. 447 // 448 status = STATUS_BUFFER_TOO_SMALL; 449 } 450 } 451 else if (status == STATUS_BUFFER_TOO_SMALL) { 452 if (m_Provider->GetMinInstanceBufferSize()) { 453 DoTraceLevelMessage( 454 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 455 "WDFWMIINSTANCE %p returned %!STATUS!, but it specified " 456 "a minimum instance size %d in its WDFWMIPROVIDER %p", 457 GetHandle(), status, m_Provider->GetMinInstanceBufferSize(), 458 m_Provider->GetHandle()); 459 DoTraceLevelMessage( 460 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 461 "This is a break in the contract. Minimum instance size " 462 "should only be used for fixed sized instances"); 463 464 FxVerifierDbgBreakPoint(GetDriverGlobals()); 465 } 466 } 467 } 468 else { 469 ASSERT(m_ExecuteMethodCallback.m_Method != NULL); 470 471 DoTraceLevelMessage( 472 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 473 "WDFWMIINSTANCE %p was queried with no query callback and supports " 474 "execute method (%p), zero bytes returned", GetHandle(), 475 m_ExecuteMethodCallback.m_Method); 476 477 status = STATUS_SUCCESS; 478 *BufferUsed = 0; 479 } 480 481 return status; 482 } 483 484 BOOLEAN 485 FxWmiInstanceExternal::IsSetInstanceSupported( 486 VOID 487 ) 488 { 489 return m_SetInstanceCallback.m_Method != NULL ? TRUE : FALSE; 490 } 491 492 _Must_inspect_result_ 493 __drv_sameIRQL 494 __drv_maxIRQL(PASSIVE_LEVEL) 495 NTSTATUS 496 FxWmiInstanceExternal::SetInstance( 497 __in ULONG InBufferSize, 498 __in_bcount(InBufferSize) PVOID InBuffer 499 ) 500 { 501 return m_SetInstanceCallback.Invoke( 502 GetDevice()->GetHandle(), 503 GetHandle(), 504 InBufferSize, 505 InBuffer 506 ); 507 } 508 509 BOOLEAN 510 FxWmiInstanceExternal::IsSetItemSupported( 511 VOID 512 ) 513 { 514 return m_SetItemCallback.m_Method != NULL ? TRUE : FALSE; 515 } 516 517 _Must_inspect_result_ 518 __drv_sameIRQL 519 __drv_maxIRQL(PASSIVE_LEVEL) 520 NTSTATUS 521 FxWmiInstanceExternal::SetItem( 522 __in ULONG DataItemId, 523 __in ULONG InBufferSize, 524 __in_bcount(InBufferSize) PVOID InBuffer 525 ) 526 { 527 return m_SetItemCallback.Invoke( 528 GetDevice()->GetHandle(), 529 GetHandle(), 530 DataItemId, 531 InBufferSize, 532 InBuffer 533 ); 534 } 535 536 BOOLEAN 537 FxWmiInstanceExternal::IsExecuteMethodSupported( 538 VOID 539 ) 540 { 541 return m_ExecuteMethodCallback.m_Method != NULL ? TRUE : FALSE; 542 } 543 544 _Must_inspect_result_ 545 __drv_sameIRQL 546 __drv_maxIRQL(PASSIVE_LEVEL) 547 NTSTATUS 548 FxWmiInstanceExternal::ExecuteMethod( 549 __in ULONG MethodId, 550 __in ULONG InBufferSize, 551 __inout ULONG OutBufferSize, 552 __drv_when(InBufferSize >= OutBufferSize, __inout_bcount(InBufferSize)) 553 __drv_when(InBufferSize < OutBufferSize, __inout_bcount(OutBufferSize)) 554 PVOID Buffer, 555 __out PULONG BufferUsed 556 ) 557 { 558 return m_ExecuteMethodCallback.Invoke( 559 GetDevice()->GetHandle(), 560 GetHandle(), 561 MethodId, 562 InBufferSize, 563 OutBufferSize, 564 Buffer, 565 BufferUsed 566 ); 567 } 568 569 FxWmiInstanceInternal::FxWmiInstanceInternal( 570 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 571 __in FxWmiInstanceInternalCallbacks* Callbacks, 572 __in FxWmiProvider* Provider 573 ) : FxWmiInstance(FxDriverGlobals, sizeof(FxWmiInstanceInternal), Provider) 574 { 575 m_QueryInstance = Callbacks->QueryInstance; 576 m_SetInstance = Callbacks->SetInstance; 577 m_SetItem = Callbacks->SetItem; 578 m_ExecuteMethod = Callbacks->ExecuteMethod; 579 } 580 581 BOOLEAN 582 FxWmiInstanceInternal::IsQueryInstanceSupported( 583 VOID 584 ) 585 { 586 return m_QueryInstance != NULL ? TRUE : FALSE; 587 } 588 589 _Must_inspect_result_ 590 __drv_sameIRQL 591 __drv_maxIRQL(PASSIVE_LEVEL) 592 NTSTATUS 593 FxWmiInstanceInternal::QueryInstance( 594 __inout ULONG OutBufferSize, 595 __out_bcount(OutBufferSize) PVOID OutBuffer, 596 __out PULONG BufferUsed 597 ) 598 { 599 return m_QueryInstance(GetDevice(), 600 this, 601 OutBufferSize, 602 OutBuffer, 603 BufferUsed); 604 } 605 606 BOOLEAN 607 FxWmiInstanceInternal::IsSetInstanceSupported( 608 VOID 609 ) 610 { 611 return m_SetInstance != NULL ? TRUE : FALSE; 612 } 613 614 _Must_inspect_result_ 615 __drv_sameIRQL 616 __drv_maxIRQL(PASSIVE_LEVEL) 617 NTSTATUS 618 FxWmiInstanceInternal::SetInstance( 619 __in ULONG InBufferSize, 620 __in_bcount(InBufferSize) PVOID InBuffer 621 ) 622 { 623 return m_SetInstance(GetDevice(), 624 this, 625 InBufferSize, 626 InBuffer); 627 } 628 629 BOOLEAN 630 FxWmiInstanceInternal::IsSetItemSupported( 631 VOID 632 ) 633 { 634 return m_SetItem != NULL ? TRUE : FALSE; 635 } 636 637 _Must_inspect_result_ 638 __drv_sameIRQL 639 __drv_maxIRQL(PASSIVE_LEVEL) 640 NTSTATUS 641 FxWmiInstanceInternal::SetItem( 642 __in ULONG DataItemId, 643 __in ULONG InBufferSize, 644 __in_bcount(InBufferSize) PVOID InBuffer 645 ) 646 { 647 return m_SetItem(GetDevice(), 648 this, 649 DataItemId, 650 InBufferSize, 651 InBuffer); 652 } 653 654 BOOLEAN 655 FxWmiInstanceInternal::IsExecuteMethodSupported( 656 VOID 657 ) 658 659 { 660 return m_ExecuteMethod != NULL ? TRUE : FALSE; 661 } 662 663 _Must_inspect_result_ 664 __drv_sameIRQL 665 __drv_maxIRQL(PASSIVE_LEVEL) 666 NTSTATUS 667 FxWmiInstanceInternal::ExecuteMethod( 668 __in ULONG MethodId, 669 __in ULONG InBufferSize, 670 __inout ULONG OutBufferSize, 671 __drv_when(InBufferSize >= OutBufferSize, __inout_bcount(InBufferSize)) 672 __drv_when(InBufferSize < OutBufferSize, __inout_bcount(OutBufferSize)) 673 PVOID Buffer, 674 __out PULONG BufferUsed 675 ) 676 { 677 return m_ExecuteMethod(GetDevice(), 678 this, 679 MethodId, 680 InBufferSize, 681 OutBufferSize, 682 Buffer, 683 BufferUsed); 684 } 685