1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxWmiIrpHandler.cpp
8 
9 Abstract:
10 
11     This module implements the wmi irp handler for the driver frameworks.
12 
13 Author:
14 
15 
16 
17 Environment:
18 
19     Kernel mode only
20 
21 Revision History:
22 
23 --*/
24 
25 #include "fxwmipch.hpp"
26 
27 extern "C" {
28 // #include "FxWmiIrpHandler.tmh"
29 }
30 
31 #ifndef WppDebug
32 #define WppDebug(a, b)
33 #endif
34 
35 const FxWmiMinorEntry FxWmiIrpHandler::m_WmiDispatchTable[] =
36 {
37     // IRP_MN_QUERY_ALL_DATA
38     { _QueryAllData, FALSE },
39 
40     // IRP_MN_QUERY_SINGLE_INSTANCE
41     { _QuerySingleInstance, TRUE },
42 
43     // IRP_MN_CHANGE_SINGLE_INSTANCE
44     { _ChangeSingleInstance, TRUE },
45 
46     // IRP_MN_CHANGE_SINGLE_ITEM
47     { _ChangeSingleItem, TRUE },
48 
49     // IRP_MN_ENABLE_EVENTS
50     { _EnableDisableEventsAndCollection, FALSE },
51 
52     // IRP_MN_DISABLE_EVENTS
53     { _EnableDisableEventsAndCollection, FALSE },
54 
55     // IRP_MN_ENABLE_COLLECTION
56     { _EnableDisableEventsAndCollection, FALSE },
57 
58     // IRP_MN_DISABLE_COLLECTION
59     { _EnableDisableEventsAndCollection, FALSE },
60 
61     // IRP_MN_REGINFO
62     { _RegInfo, FALSE },
63 
64     // IRP_MN_EXECUTE_METHOD
65     { _ExecuteMethod, TRUE },
66 
67     // 0xA is reseverved
68     { NULL, FALSE },
69 
70     // IRP_MN_REGINFO_EX
71     { _RegInfo, FALSE },
72 };
73 
74 VOID
CheckAssumptions(VOID)75 FxWmiIrpHandler::CheckAssumptions(
76     VOID
77     )
78 {
79     WDFCASSERT(sizeof(m_WmiDispatchTable)/sizeof(m_WmiDispatchTable[0]) ==
80                IRP_MN_REGINFO_EX + 1);
81 }
82 
FxWmiIrpHandler(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in FxDevice * Device,__in WDFTYPE Type)83 FxWmiIrpHandler::FxWmiIrpHandler(
84     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
85     __in FxDevice *Device,
86     __in WDFTYPE Type
87     ) :
88     FxPackage(FxDriverGlobals, Device, Type),
89     m_NumProviders(0), m_RegisteredState(WmiUnregistered),
90     m_WorkItem(NULL), m_WorkItemEvent(NULL), m_WorkItemQueued(FALSE),
91     m_UpdateCount(1) // bias m_UpdateCount to 1, Deregister routine will
92                      // decrement this.
93 {
94     InitializeListHead(&m_ProvidersListHead);
95 }
96 
~FxWmiIrpHandler()97 FxWmiIrpHandler::~FxWmiIrpHandler()
98 {
99     //
100     // If the device could not get past AddDevice or failed the initial start
101     // device, we will be unregistered.  Otherwise we should be cleaned up.
102     //
103     ASSERT(m_RegisteredState != WmiRegistered);
104 
105     ASSERT(IsListEmpty(&m_ProvidersListHead));
106 
107     if (m_WorkItem != NULL) {
108         IoFreeWorkItem(m_WorkItem);
109     }
110 }
111 
112 _Must_inspect_result_
113 NTSTATUS
PostCreateDeviceInitialize(VOID)114 FxWmiIrpHandler::PostCreateDeviceInitialize(
115     VOID
116     )
117 {
118     m_WorkItem = IoAllocateWorkItem(GetDevice()->GetDeviceObject());
119     if (m_WorkItem == NULL) {
120         return STATUS_INSUFFICIENT_RESOURCES;
121     }
122 
123     return STATUS_SUCCESS;
124 }
125 
126 _Must_inspect_result_
127 NTSTATUS
Register(VOID)128 FxWmiIrpHandler::Register(
129     VOID
130     )
131 {
132     NTSTATUS status;
133     KIRQL irql;
134 
135     //
136     // We rely on the PnP state machine to manage our state transitions properly
137     // so that we don't have to do any state checking here.
138     //
139     Lock(&irql);
140     ASSERT(m_RegisteredState == WmiUnregistered ||
141               m_RegisteredState == WmiDeregistered);
142     m_RegisteredState = WmiRegistered;
143     Unlock(irql);
144 
145     status = IoWMIRegistrationControl(GetDevice()->GetDeviceObject(),
146                                       WMIREG_ACTION_REGISTER);
147 
148     if (!NT_SUCCESS(status)) {
149         DoTraceLevelMessage(
150             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
151             "could not register WMI with OS, %!STATUS!", status);
152 
153         Lock(&irql);
154         m_RegisteredState = WmiUnregistered;
155         Unlock(irql);
156     }
157 
158     return status;
159 }
160 
161 VOID
Deregister(VOID)162 FxWmiIrpHandler::Deregister(
163     VOID
164     )
165 {
166     FxCREvent event;
167     KIRQL irql;
168     BOOLEAN call;
169 
170     call = FALSE;
171 
172     Lock(&irql);
173     if (m_RegisteredState == WmiRegistered) {
174         m_RegisteredState = WmiDeregistered;
175 
176         if (m_WorkItemQueued) {
177 
178 
179 
180 
181 
182             m_WorkItemEvent = (PKEVENT)event.GetEvent();
183         }
184 
185         call = TRUE;
186     }
187     Unlock(irql);
188 
189     if (m_WorkItemEvent != NULL) {
190         event.EnterCRAndWaitAndLeave();
191     }
192 
193     if (call) {
194         NTSTATUS status;
195 
196         //
197         // Per WMI rules, there should not be any call to update WMI
198         // registration after we have deregistered because that can lead to
199         // deadlock in Pnp, so before we go ahead to deregister, let's ensure
200         // that. This will wait for any pending updates to happen.
201         //
202         DecrementUpdateCountAndWait();
203 
204         status = IoWMIRegistrationControl(GetDevice()->GetDeviceObject(),
205                                           WMIREG_ACTION_DEREGISTER);
206 
207         if (!NT_SUCCESS(status)) {
208             DoTraceLevelMessage(
209                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
210                 "failure deregistering WMI with OS, %!STATUS!", status);
211         }
212     }
213 }
214 
215 VOID
Cleanup(VOID)216 FxWmiIrpHandler::Cleanup(
217     VOID
218     )
219 {
220     KIRQL irql;
221 
222     Lock(&irql);
223     m_RegisteredState = WmiCleanedUp;
224     Unlock(irql);
225 }
226 
227 VOID
ResetStateForPdoRestart(VOID)228 FxWmiIrpHandler::ResetStateForPdoRestart(
229     VOID
230     )
231 {
232     KIRQL irql;
233 
234     Lock(&irql);
235 
236     //
237     // We can reach this state in 2 ways:
238     // 1. PDO went through Init->Started->Removed->Started transition
239     // 2. PDO went through Init-><SomeFailedState>->Started state (e.g.
240     // Init->Ejected->EjectFailed->Started) transition.
241     //
242     // So, WMI registration state would either be Deregistered due to #1
243     // or, Unregistered due to #2. Also, update count would be 0 in case of #1
244     // and 1 in case of #2.
245     //
246     ASSERT(m_RegisteredState == WmiUnregistered ||
247               m_RegisteredState == WmiDeregistered);
248     ASSERT(m_UpdateCount == 0 || m_UpdateCount == 1);
249 
250     //
251     // Update count is biased to 1 when created so do the same here.
252     //
253     m_UpdateCount = 1;
254     m_UpdateEvent.Initialize();
255     Unlock(irql);
256 }
257 
258 _Must_inspect_result_
259 NTSTATUS
AddProviderLocked(__in FxWmiProvider * Provider,__in KIRQL OldIrql,__out_opt PBOOLEAN Update)260 FxWmiIrpHandler::AddProviderLocked(
261     __in      FxWmiProvider* Provider,
262     __in      KIRQL OldIrql,
263     __out_opt PBOOLEAN Update
264     )
265 {
266     BOOLEAN update;
267 
268     update = FALSE;
269 
270     switch (m_RegisteredState) {
271     case WmiRegistered:
272         if (Provider->m_Flags & WdfWmiProviderTracing) {
273             update = TRUE;
274         }
275     case WmiUnregistered:
276         break;
277 
278     case WmiDeregistered:
279         return STATUS_INVALID_DEVICE_STATE;
280     }
281 
282     //
283     // Didn't find it in the list, add it
284     //
285     m_NumProviders++;
286     InsertTailList(&m_ProvidersListHead, &Provider->m_ListEntry);
287 
288     if (update) {
289         update = DeferUpdateLocked(OldIrql);
290 
291         if (Update != NULL) {
292             *Update = update;
293         }
294     }
295 
296     return STATUS_SUCCESS;
297 }
298 
299 BOOLEAN
DeferUpdateLocked(__in KIRQL OldIrql)300 FxWmiIrpHandler::DeferUpdateLocked(
301     __in KIRQL OldIrql
302     )
303 {
304     BOOLEAN checkQueue;
305 
306     checkQueue = FALSE;
307 
308     //
309     // Check to see if the caller is going to return to something > PASSIVE_LEVEL.
310     // If so, then always defer to the workitem.
311     //
312     if (OldIrql > PASSIVE_LEVEL) {
313         checkQueue = TRUE;
314     }
315     else {
316         //
317         // At passive level and updates are allowed, indicate to the caller to
318         // update. The caller will do registration update outside of lock but
319         // update count needs to be incremented under lock, so this is done here.
320         //
321         IncrementUpdateCount();
322         return TRUE;
323     }
324 
325     if (checkQueue && m_WorkItemQueued == FALSE) {
326         //
327         // we are going to queue a workitem which will do registration update,
328         // so increment the update count. Note that one work item may correspond
329         // to multiple requests to update (since if the workitem is already
330         // queued, it's not queued again).
331         //
332         IncrementUpdateCount();
333 
334         m_WorkItemQueued = TRUE;
335         IoQueueWorkItem(m_WorkItem,
336                         _UpdateGuids,
337                         DelayedWorkQueue,
338                         this);
339     }
340 
341     return FALSE;
342 }
343 
344 VOID
_UpdateGuids(__in PDEVICE_OBJECT DeviceObject,__in PVOID Context)345 FxWmiIrpHandler::_UpdateGuids(
346     __in PDEVICE_OBJECT DeviceObject,
347     __in PVOID Context
348     )
349 {
350     FxWmiIrpHandler* pThis;
351     KIRQL irql;
352 
353     UNREFERENCED_PARAMETER(DeviceObject);
354 
355     pThis = (FxWmiIrpHandler*)Context;
356 
357     pThis->UpdateGuids();
358 
359     pThis->Lock(&irql);
360     pThis->m_WorkItemQueued = FALSE;
361 
362     if (pThis->m_WorkItemEvent != NULL) {
363         KeSetEvent(pThis->m_WorkItemEvent, FALSE, IO_NO_INCREMENT);
364     }
365     pThis->Unlock(irql);
366 }
367 
368 VOID
UpdateGuids(VOID)369 FxWmiIrpHandler::UpdateGuids(
370     VOID
371     )
372 {
373     NTSTATUS status;
374 
375     status = IoWMIRegistrationControl(GetDevice()->GetDeviceObject(),
376                                       WMIREG_ACTION_UPDATE_GUIDS);
377 
378     if (!NT_SUCCESS(status)) {
379         DoTraceLevelMessage(
380             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIO,
381             "IoWMIRegistrationControl DevObj %p, for UpdateGuids failed, %!STATUS!",
382             GetDevice()->GetDeviceObject(), status);
383 
384         //
385         // Just drop the error
386         //
387     }
388 
389     //
390     // The caller incremented the update count when it decided to do the update.
391     // Decrement the count now that we have completed registration update.
392     //
393     DecrementUpdateCount();
394 }
395 
396 _Must_inspect_result_
397 NTSTATUS
AddProvider(__in FxWmiProvider * Provider,__out_opt PBOOLEAN Update)398 FxWmiIrpHandler::AddProvider(
399     __in      FxWmiProvider* Provider,
400     __out_opt PBOOLEAN Update
401     )
402 {
403     NTSTATUS status;
404     KIRQL irql;
405 
406     Lock(&irql);
407 
408     if (IsListEmpty(&Provider->m_ListEntry) == FALSE ||
409         FindProviderLocked(Provider->GetGUID()) != NULL) {
410         status = STATUS_OBJECT_NAME_EXISTS;
411     }
412     else {
413         status = AddProviderLocked(Provider, irql, Update);
414     }
415     Unlock(irql);
416 
417     return status;
418 }
419 
420 _Must_inspect_result_
421 NTSTATUS
AddPowerPolicyProviderAndInstance(__in PWDF_WMI_PROVIDER_CONFIG ProviderConfig,__in FxWmiInstanceInternalCallbacks * InstanceCallbacks,__inout FxWmiInstanceInternal ** Instance)422 FxWmiIrpHandler::AddPowerPolicyProviderAndInstance(
423     __in    PWDF_WMI_PROVIDER_CONFIG ProviderConfig,
424     __in    FxWmiInstanceInternalCallbacks* InstanceCallbacks,
425     __inout FxWmiInstanceInternal** Instance
426     )
427 {
428     FxWmiProvider* pProvider;
429     FxWmiInstanceInternal* pInstance;
430     NTSTATUS status;
431     KIRQL irql;
432     BOOLEAN providerAllocated, providerAdded, update;
433 
434     providerAllocated = FALSE;
435     providerAdded = FALSE;
436     update = FALSE;
437 
438     pInstance = NULL;
439 
440     status = STATUS_SUCCESS;
441 
442     Lock(&irql);
443 
444     pProvider = FindProviderLocked(&ProviderConfig->Guid);
445 
446     if (pProvider == NULL) {
447         pProvider = new (GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
448             FxWmiProvider(GetDriverGlobals(), ProviderConfig, GetDevice());
449 
450         if (pProvider == NULL) {
451             status = STATUS_INSUFFICIENT_RESOURCES;
452             goto Done;
453         }
454 
455         providerAllocated = TRUE;
456 
457         status = AddProviderLocked(pProvider, irql);
458         if (!NT_SUCCESS(status)) {
459             goto Done;
460         }
461 
462         providerAdded = TRUE;
463 
464         status = pProvider->AssignParentObject(GetDevice());
465         if (!NT_SUCCESS(status)) {
466             goto Done;
467         }
468     }
469     else if (pProvider->m_NumInstances > 0 &&
470              (FxIsEqualGuid(pProvider->GetGUID(), &GUID_POWER_DEVICE_ENABLE) ||
471               FxIsEqualGuid(pProvider->GetGUID(), &GUID_POWER_DEVICE_WAKE_ENABLE))) {
472 
473         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIO,
474                             "WMI Guid already registered by client driver");
475         status = STATUS_WMI_GUID_DISCONNECTED;
476     }
477 
478     if (NT_SUCCESS(status)) {
479         pInstance = new (GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
480             FxWmiInstanceInternal(GetDriverGlobals(), InstanceCallbacks, pProvider);
481 
482         if (pInstance != NULL) {
483             status = pInstance->AssignParentObject(pProvider);
484         }
485         else {
486             status = STATUS_INSUFFICIENT_RESOURCES;
487         }
488 
489         if (NT_SUCCESS(status) &&
490             InterlockedCompareExchangePointer((PVOID*) Instance,
491                                               pInstance,
492                                               NULL) != NULL) {
493             //
494             // Some other thread got here first and created an instance, just
495             // bail out.  the caller will handle this specific return value
496             // gracefully.
497             //
498             status = STATUS_OBJECT_NAME_COLLISION;
499         }
500         else {
501             //
502             // We are the first one to set the value, good for us.
503             //
504             DO_NOTHING();
505         }
506     }
507 
508     //
509     // Add the instance only if we are successful to this point
510     //
511     if (NT_SUCCESS(status)) {
512         //
513         // Passing FALSE indicates that this instance cannot be in the list
514         // when added.
515         //
516         status = pProvider->AddInstanceLocked(
517             pInstance,
518             FALSE,
519             &update,
520             FxWmiProvider::AddInstanceToHead
521             );
522     }
523 
524 Done:
525     if (NT_SUCCESS(status)) {
526         if (update) {
527             update = DeferUpdateLocked(irql);
528         }
529     }
530     else if (providerAdded) {
531         RemoveProviderLocked(pProvider);
532     }
533 
534     Unlock(irql);
535 
536     if (NT_SUCCESS(status)) {
537         if (update) {
538             UpdateGuids();
539         }
540     }
541     else {
542         if (pInstance != NULL) {
543             pInstance->DeleteObject();
544         }
545 
546         if (providerAllocated) {
547             pProvider->DeleteObject();
548         }
549     }
550 
551     return status;
552 }
553 
554 VOID
RemoveProvider(__in FxWmiProvider * Provider)555 FxWmiIrpHandler::RemoveProvider(
556     __in FxWmiProvider* Provider
557     )
558 {
559     KIRQL irql;
560 
561     //
562     // No need to update via IoWMIRegistrationControl because this is only
563     // called in the error path of creating a provider.
564     //
565     Lock(&irql);
566     RemoveProviderLocked(Provider);
567     Unlock(irql);
568 }
569 
570 VOID
RemoveProviderLocked(__in FxWmiProvider * Provider)571 FxWmiIrpHandler::RemoveProviderLocked(
572     __in FxWmiProvider* Provider
573     )
574 {
575     m_NumProviders--;
576     RemoveEntryList(&Provider->m_ListEntry);
577     InitializeListHead(&Provider->m_ListEntry);
578 }
579 
580 _Must_inspect_result_
581 FxWmiProvider*
FindProviderLocked(__in LPGUID Guid)582 FxWmiIrpHandler::FindProviderLocked(
583     __in LPGUID Guid
584     )
585 {
586     FxWmiProvider* pFound;
587     PLIST_ENTRY ple;
588 
589     pFound = NULL;
590 
591     for (ple = m_ProvidersListHead.Flink;
592          ple != &m_ProvidersListHead;
593          ple = ple->Flink) {
594 
595         FxWmiProvider* pProvider;
596 
597         pProvider = CONTAINING_RECORD(ple, FxWmiProvider, m_ListEntry);
598 
599         if (RtlCompareMemory(&pProvider->m_Guid,
600                              Guid,
601                              sizeof(GUID)) == sizeof(GUID)) {
602             pFound = pProvider;
603             break;
604         }
605     }
606 
607     return pFound;
608 }
609 
610 _Must_inspect_result_
611 FxWmiProvider*
FindProviderReferenced(__in LPGUID Guid,__in PVOID Tag)612 FxWmiIrpHandler::FindProviderReferenced(
613     __in LPGUID Guid,
614     __in PVOID Tag
615     )
616 {
617     FxWmiProvider* pProvider;
618     KIRQL irql;
619 
620     Lock(&irql);
621     pProvider = FindProviderLocked(Guid);
622     if (pProvider != NULL) {
623         pProvider->ADDREF(Tag);
624     }
625     Unlock(irql);
626 
627     return pProvider;
628 }
629 
630 _Must_inspect_result_
631 NTSTATUS
Dispatch(__in PIRP Irp)632 FxWmiIrpHandler::Dispatch(
633     __in PIRP Irp
634     )
635 {
636     PFX_DRIVER_GLOBALS pFxDriverGlobals;
637     FxWmiProvider* pProvider;
638     FxWmiInstance* pInstance;
639     PIO_STACK_LOCATION stack;
640     PDEVICE_OBJECT pAttached;
641     NTSTATUS status;
642     PVOID pTag;
643     ULONG instanceIndex;
644     KIRQL irql;
645     BOOLEAN handled, completeNow;
646     UCHAR minor;
647 
648     pFxDriverGlobals = GetDriverGlobals();
649 
650     FX_TRACK_DRIVER(pFxDriverGlobals);
651 
652     stack = IoGetCurrentIrpStackLocation(Irp);
653     minor = stack->MinorFunction;
654     pTag = UlongToPtr(minor);
655     status = Irp->IoStatus.Status;
656 
657     pProvider = NULL;
658     pInstance = NULL;
659 
660     handled = FALSE;
661     completeNow = FALSE;
662 
663     DoTraceLevelMessage(
664         GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIO,
665         "WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_SYSTEM_CONTROL, %!sysctrl! IRP 0x%p",
666         m_Device->GetHandle(), m_Device->GetDeviceObject(), minor, Irp);
667 
668     //
669     // Verify the minor code is within range, there is hole in the table at 0xA.
670     // This check works around the hole in the table.
671     //
672     if (minor > IRP_MN_EXECUTE_METHOD && minor != IRP_MN_REGINFO_EX) {
673         goto Done;
674     }
675 
676     //
677     // If the irp is not targetted at this device, send it down the stack
678     //
679     if (stack->Parameters.WMI.ProviderId != (UINT_PTR) m_Device->GetDeviceObject()) {
680         goto Done;
681     }
682 
683     if (minor == IRP_MN_REGINFO || minor == IRP_MN_REGINFO_EX) {
684         status = STATUS_SUCCESS;
685     }
686     else {
687         Lock(&irql);
688 
689         pProvider = FindProviderLocked((LPGUID)stack->Parameters.WMI.DataPath);
690 
691         if (pProvider != NULL) {
692             status = STATUS_SUCCESS;
693         }
694         else {
695             //
696             // check for WMI tracing (no pProvider)
697             //
698             status = STATUS_WMI_GUID_NOT_FOUND;
699         }
700 
701         if (NT_SUCCESS(status) && m_WmiDispatchTable[minor].CheckInstance) {
702             PWNODE_SINGLE_INSTANCE pSingle;
703 
704             pSingle = (PWNODE_SINGLE_INSTANCE) stack->Parameters.WMI.Buffer;
705 
706             instanceIndex = pSingle->InstanceIndex;
707 
708             //
709             // Also possible bits set in Flags related to instance names
710             // WNODE_FLAG_PDO_INSTANCE_NAMES
711             //
712             if (pSingle->WnodeHeader.Flags & WNODE_FLAG_STATIC_INSTANCE_NAMES) {
713                 //
714                 // Try to get the instance
715                 //
716                 pInstance = pProvider->GetInstanceReferencedLocked(
717                     instanceIndex, pTag);
718 
719                 if (pInstance == NULL) {
720                     status = STATUS_WMI_INSTANCE_NOT_FOUND;
721                 }
722             }
723             else {
724 
725 
726 
727 
728 
729 
730                 status = STATUS_WMI_INSTANCE_NOT_FOUND;
731             }
732         }
733 
734         if (NT_SUCCESS(status)) {
735             pProvider->ADDREF(pTag);
736         }
737         else {
738             //
739             // NULL out the provider so we don't deref it later.  We could not
740             // use if (pProvider != NULL && NT_SUCCESS(status) in the Done: block
741             // because we could have success here, but the dispatch function
742             // returns error.
743             //
744             pProvider = NULL;
745         }
746 
747         Unlock(irql);
748 
749         if (!NT_SUCCESS(status)) {
750             Irp->IoStatus.Status = status;
751             completeNow = TRUE;
752         }
753     }
754 
755     if (NT_SUCCESS(status) && m_WmiDispatchTable[minor].Handler != NULL) {
756         status = m_WmiDispatchTable[minor].Handler(this,
757                                                    Irp,
758                                                    pProvider,
759                                                    pInstance);
760         handled = TRUE;
761     }
762 
763 Done:
764     if (pInstance != NULL) {
765         pInstance->RELEASE(pTag);
766         pInstance = NULL;
767     }
768 
769     if (pProvider != NULL) {
770         pProvider->RELEASE(pTag);
771         pProvider = NULL;
772     }
773 
774     if (handled == FALSE) {
775         pAttached = m_Device->GetAttachedDevice();
776         if (completeNow || pAttached == NULL) {
777             //
778             // Sent to a PDO, error in the FDO handling, or controller devobj
779             // style devobj, complete it here
780             //
781             status = Irp->IoStatus.Status;
782             IoCompleteRequest(Irp, IO_NO_INCREMENT);
783         }
784         else {
785             //
786             // Request sent to PNP device object that is not a PDO, send down
787             // the stack
788             //
789             IoSkipCurrentIrpStackLocation(Irp);
790             status = IoCallDriver(pAttached, Irp);
791         }
792     }
793 
794     //
795     // Only release the remove lock *after* we have removed the thread entry
796     // from the list because the list lifetime is tied to the FxDevice lifetime
797     // and the remlock controls the lifetime of FxDevice.
798     //
799     // Since we never pend the wmi request, we can release the remove lock in
800     // this Dispatch routine.  If we ever pended the IRPs, this would have to
801     // have some more logic involved.
802     //
803     IoReleaseRemoveLock(
804         &FxDevice::_GetFxWdmExtension(m_Device->GetDeviceObject())->IoRemoveLock,
805         Irp
806         );
807 
808     return status;
809 }
810 
811 _Must_inspect_result_
812 NTSTATUS
_QueryAllData(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in_opt FxWmiInstance * Instance)813 FxWmiIrpHandler::_QueryAllData(
814     __in FxWmiIrpHandler* This,
815     __in PIRP Irp,
816     __in FxWmiProvider* Provider,
817     __in_opt FxWmiInstance* Instance
818     )
819 /*++
820 
821 Routine Description:
822     Handles querying all instances for data.  In the case where there is not
823     enough space in the buffer, we query each instance for the required amount
824     of buffer space and report that.
825 
826 Arguments:
827     This - device instance
828 
829     Irp - WMI request for query all data
830 
831     Providre - the provider being queried
832 
833     Instance - ignored, NULL
834 
835 Return Value:
836     STATUS_BUFFER_TOO_SMALL in case the buffer is not large enough
837     STATUS_SUCCESS on a successful query
838     or other values depending on the client driver
839 
840   --*/
841 {
842     POFFSETINSTANCEDATAANDLENGTH pOffsets;
843     PWNODE_ALL_DATA pNodeData;
844     PIO_STACK_LOCATION stack;
845     PUCHAR pData;
846     NTSTATUS status, addStatus;
847     ULONG instanceCount, dataBlockOffset, lengthArraySize, i, sizeRemaining,
848           lastAdjustment, bufferUsed, tempOffset;
849     KIRQL irql;
850     BOOLEAN tooSmall;
851 
852     UNREFERENCED_PARAMETER(Instance);
853 
854     status = STATUS_SUCCESS;
855 
856     stack = IoGetCurrentIrpStackLocation(Irp);
857     pNodeData = (PWNODE_ALL_DATA) stack->Parameters.WMI.Buffer;
858 
859     lastAdjustment = 0;
860     pData = NULL;
861     dataBlockOffset = 0;
862 
863     //
864     // This either the amount of buffer used on successful queries or the
865     // amount of buffer required if the buffer is not big enough.
866     //
867     bufferUsed = 0;
868 
869     tooSmall = FALSE;
870 
871     if (stack->Parameters.WMI.BufferSize < sizeof(WNODE_ALL_DATA)) {
872         status = STATUS_UNSUCCESSFUL;
873         goto Done;
874     }
875 
876 
877 
878 
879 
880 
881 
882     //
883     // All of the provider's access is guarded by the irp handler's lock
884     //
885     This->Lock(&irql);
886     instanceCount = Provider->m_NumInstances;
887     This->Unlock(irql);
888 
889 
890 
891 
892 
893 
894 
895 
896 
897 
898 
899     if (instanceCount == 0) {
900         status = STATUS_WMI_INSTANCE_NOT_FOUND;
901         DoTraceLevelMessage(
902             This->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
903             "Failing QueryAllData since no instances found for "
904             "WDFWMIPROVIDER %p, %!STATUS!",
905             Provider->GetHandle(), status);
906         bufferUsed = 0;
907         goto Done;
908     }
909 
910     DoTraceLevelMessage(
911         This->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
912         "WDFWMIPROVIDER %p QueryAllData, num instances %d",
913         Provider->GetHandle(), instanceCount);
914 
915     pNodeData->InstanceCount = instanceCount;
916 
917 
918 
919 
920 
921 
922     pNodeData->WnodeHeader.Flags &= ~WNODE_FLAG_FIXED_INSTANCE_SIZE;
923 
924     //
925     // Since value of instanceCount is not limited, do overflow-safe addition
926     // and multiplication
927     //
928     // lengthArraySize = instanceCount * sizeof(OFFSETINSTANCEDATAANDLENGTH);
929     status = RtlULongMult(instanceCount,
930                           sizeof(OFFSETINSTANCEDATAANDLENGTH),
931                           &lengthArraySize);
932 
933     if (NT_SUCCESS(status)) {
934         // dataBlockOffset =
935         // FIELD_OFFSET(WNODE_ALL_DATA, OffsetInstanceDataAndLength) + lengthArraySize;
936         status = RtlULongAdd(
937             FIELD_OFFSET(WNODE_ALL_DATA, OffsetInstanceDataAndLength),
938             lengthArraySize,
939             &tempOffset);
940 
941         if (NT_SUCCESS(status)) {
942             dataBlockOffset = (ULONG) WDF_ALIGN_SIZE_UP(
943                 tempOffset, MEMORY_ALLOCATION_ALIGNMENT);
944 
945             if (dataBlockOffset < tempOffset) {
946                 status = STATUS_INTEGER_OVERFLOW;
947             }
948         }
949     }
950 
951     if (!NT_SUCCESS(status)) {
952         DoTraceLevelMessage(
953             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
954             "Failing QueryAllData since integer overflow occured using"
955             " provider instance count %d for WDFWMIPROVIDER %p, %!STATUS!",
956             instanceCount, Provider->GetHandle(), status);
957         bufferUsed = 0;
958         goto Done;
959     }
960 
961     pNodeData->DataBlockOffset = dataBlockOffset;
962 
963     if (dataBlockOffset <= stack->Parameters.WMI.BufferSize) {
964         pOffsets = &pNodeData->OffsetInstanceDataAndLength[0];
965         sizeRemaining = stack->Parameters.WMI.BufferSize - dataBlockOffset;
966         pData = (PUCHAR) WDF_PTR_ADD_OFFSET(pNodeData, dataBlockOffset);
967     }
968     else {
969         //
970         // There is not enough room in the WNODE to complete the query, but
971         // we still want to query all the instances to see how big a buffer the
972         // caller should resend.
973         //
974         pOffsets = NULL;
975         pData = NULL;
976         sizeRemaining = 0;
977 
978         //
979         // We are in the too small case
980         //
981         tooSmall = TRUE;
982         status = STATUS_BUFFER_TOO_SMALL;
983     }
984 
985     if (instanceCount > 0 && Provider->GetMinInstanceBufferSize() != 0) {
986         ULONG size, minSizeAdjusted;
987 
988         size = 0;
989 
990         minSizeAdjusted = (ULONG) WDF_ALIGN_SIZE_UP(
991             Provider->GetMinInstanceBufferSize(),
992             MEMORY_ALLOCATION_ALIGNMENT
993             );
994 
995         //
996         // Quick size check to make sure there is enough buffer for all of the
997         // instances.  We round up all but the last instance's minimum size so
998         // that we can make sure we have enough room for an aligned bufer for
999         // each instance.
1000         //
1001         // All but the last instance are using the adjusted size.  In the end,
1002         // we will have:
1003         //
1004         // size = minSizeAdjusted * (instanceCount-1) +
1005         //        Provider->GetMinInstanceBufferSize();
1006         //
1007         // NOTE:  we do not care about instances which do not support query
1008         //        data.  We don't care b/c by computing the total size of all
1009         //        instances we compute the maximum sized buffer needed, and if
1010         //        a few instances  do no support query data, then the buffer
1011         //        will just be too large, never too small.
1012         //
1013         status = RtlULongMult(minSizeAdjusted, instanceCount - 1, &size);
1014         if (!NT_SUCCESS(status)) {
1015             goto Done;
1016         }
1017 
1018         status = RtlULongAdd(size, Provider->GetMinInstanceBufferSize(), &size);
1019         if (!NT_SUCCESS(status)) {
1020             goto Done;
1021         }
1022 
1023         //
1024         // Since the client indicated that that each block as a minimum instance
1025         // size (which in reality is the fixed size, variable sized instnaces
1026         // should report a minimum instance size of zero), we can compute the
1027         // required buffer size w/out querying each instance for the required
1028         // size.
1029         //
1030         if (sizeRemaining < size) {
1031             //
1032             // bufferUsed in the buffer too small case indicates how many bytes
1033             // we want the caller to allocate.
1034             //
1035             bufferUsed = size;
1036             status = STATUS_BUFFER_TOO_SMALL;
1037             goto Done;
1038         }
1039     }
1040 
1041     for (i = 0; i < instanceCount; i++) {
1042         FxWmiInstance* pInstance;
1043 
1044         //
1045         // In the case where we have a minimum instance size, we should have
1046         // verified correctly above to have enough buffer.
1047         //
1048         ASSERT(sizeRemaining >= Provider->GetMinInstanceBufferSize());
1049 
1050         pInstance = Provider->GetInstanceReferenced(i, Irp);
1051 
1052         if (pInstance == NULL) {
1053             break;
1054         }
1055 
1056         if (pInstance->IsQueryInstanceSupported()) {
1057             ULONG tmpSize;
1058 
1059             tmpSize = 0;
1060 
1061             status = pInstance->QueryInstance(sizeRemaining, pData, &tmpSize);
1062 
1063             if (NT_SUCCESS(status) || status == STATUS_BUFFER_TOO_SMALL) {
1064                 ULONG adjustedSize;
1065 
1066                 //
1067                 // We calculate the size in both cases.  In the NT_SUCCESS case,
1068                 // it is the amount of buffer used. In the too small case, it is
1069                 // the amount of buffer required for a subsequent query.
1070                 //
1071                 adjustedSize = (ULONG) WDF_ALIGN_SIZE_UP(
1072                     tmpSize, MEMORY_ALLOCATION_ALIGNMENT
1073                     );
1074 
1075                 if (adjustedSize < tmpSize) {
1076                     //
1077                     // Overflow, adjustedSize will be >= tmpSize in the normal
1078                     // case.
1079                     //
1080                     status = STATUS_INTEGER_OVERFLOW;
1081                     DoTraceLevelMessage(
1082                         This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1083                         "WDFWMIINSTNACE %p queried, returned a buffer size of %d,"
1084                         "but it could not be rounded up, %!STATUS!",
1085                         pInstance->GetHandle(), tmpSize, status);
1086                     goto QueryError;
1087                 }
1088 
1089                 //
1090                 // Keep track of how much we adjusted up the size on the last
1091                 // instance so that when compute the total buffer used, we
1092                 // do not include the final size adjustment.
1093                 //
1094                 lastAdjustment = adjustedSize - tmpSize;
1095 
1096                 //
1097                 // We only write the offset and data if we have not yet
1098                 // encountered an instance where the buffer size was too small.
1099                 //
1100                 if (NT_SUCCESS(status) && tooSmall == FALSE) {
1101                     //
1102                     // dataBlockOffset is where we are currently at in the buffer
1103                     //
1104                     pOffsets[i].LengthInstanceData = tmpSize;
1105                     pOffsets[i].OffsetInstanceData = dataBlockOffset;
1106 
1107                     pData += adjustedSize;
1108                 }
1109                 else {
1110                     tooSmall = TRUE;
1111                 }
1112 
1113                 //
1114                 // Compute how much buffer we have left and our offset into it.
1115                 //
1116                 if (adjustedSize <= sizeRemaining) {
1117                     sizeRemaining -= adjustedSize;
1118 
1119                     //
1120                     // dataBlockOffset += adjustedSize;
1121                     //
1122                     addStatus = RtlULongAdd(dataBlockOffset,
1123                                             adjustedSize,
1124                                             &dataBlockOffset);
1125                 }
1126                 else {
1127                     //
1128                     // dataBlockOffset += sizeRemaining;
1129                     //
1130                     addStatus = RtlULongAdd(dataBlockOffset,
1131                                             sizeRemaining,
1132                                             &dataBlockOffset);
1133                     sizeRemaining = 0;
1134                 }
1135 
1136                 if (!NT_SUCCESS(addStatus)) {
1137                     status = addStatus;
1138                     DoTraceLevelMessage(
1139                         This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1140                         "WDFWMIPROVIDER %p, arithmetic overflow in computing "
1141                         "block offset, %!STATUS!", Provider->GetHandle(),
1142                         status);
1143                     goto QueryError;
1144                 }
1145 
1146                 //
1147                 // Compute how much buffer space we have used or require the
1148                 // caller to provide.  This is just the count of bytes for the
1149                 // data queried, it does not yet include the array size required
1150                 // to report offset & lengths.
1151                 //
1152                 // bufferUsed += adjustedSize;
1153                 //
1154                 addStatus = RtlULongAdd(bufferUsed, adjustedSize, &bufferUsed);
1155 
1156                 if (!NT_SUCCESS(addStatus)) {
1157                     status = addStatus;
1158                     DoTraceLevelMessage(
1159                         This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1160                         "WDFWMIPROVIDER %p, arithmetic overflow in computing "
1161                         "buffer consumed(%d+%d), %!STATUS!",
1162                         Provider->GetHandle(), bufferUsed, adjustedSize, status);
1163                     goto QueryError;
1164                 }
1165             }
1166         }
1167         else if (pOffsets != NULL) {
1168             //
1169             // Indicate no buffer and the current offset.  If there any
1170             // other instances after this one, they will share the same
1171             // offset.
1172             //
1173             pOffsets[i].LengthInstanceData = 0;
1174             pOffsets[i].OffsetInstanceData = dataBlockOffset;
1175         }
1176 
1177 QueryError:
1178         pInstance->RELEASE(Irp);
1179 
1180         //
1181         // We continue on STATUS_BUFFER_TOO_SMALL so that we can ask each
1182         // instance the amount of buffer it needs.
1183         //
1184         if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {
1185             break;
1186         }
1187     }
1188 
1189     //
1190     // We can have STATUS_BUFFER_TOO_SMALL if the last instance could not fit
1191     // its data into the buffer or have success if the last instance could write
1192     // its data, but a previous instance could not.
1193     //
1194     if (status == STATUS_BUFFER_TOO_SMALL || (NT_SUCCESS(status) && tooSmall)) {
1195         //
1196         // Since we align up the size of each block to MEMORY_ALLOCATION_ALIGNMENT,
1197         // the total buffer size is possibly adjusted up too high.  Subtract the
1198         // last adjustment made for alignment when computing the instance size
1199         // so that our total size is not rounded up.
1200         //
1201         // CompleteWmiQueryAllDataRequest will take care of adding enough space
1202         // to the requested size to account for the array of instance offsets
1203         // and lengths.
1204         //
1205         bufferUsed -=lastAdjustment;
1206 
1207         status = STATUS_BUFFER_TOO_SMALL;
1208 
1209         DoTraceLevelMessage(
1210             This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1211             "WDFWMIPROVIDER %p QueryAllData returning %!STATUS!, requesting "
1212             "buffer size of 0x%x", Provider->GetHandle(), status, bufferUsed);
1213     }
1214     else if (NT_SUCCESS(status)) {
1215         //
1216         // Compute the total amount of buffer used.  dataBlockOffset is the
1217         // offset of the next instance, which is one past the end, so just
1218         // compute the distance from the start.
1219         //
1220         // Since align up the size of each block to MEMORY_ALLOCATION_ALIGNMENT,
1221         // the total buffer size is possibly adjusted up too high.  Subtract the
1222         // last adjustment made for alignment when computing the instance size
1223         // so that our total size is not rounded up.
1224         //
1225         ASSERT(dataBlockOffset >= pNodeData->DataBlockOffset);
1226         ASSERT(dataBlockOffset - pNodeData->DataBlockOffset >= lastAdjustment);
1227         bufferUsed = dataBlockOffset - pNodeData->DataBlockOffset - lastAdjustment;
1228     }
1229     else {
1230         DoTraceLevelMessage(
1231             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1232             "WDFWMIPROVIDER %p QueryAllData returning %!STATUS!",
1233             Provider->GetHandle(), status);
1234 
1235         bufferUsed = 0;
1236     }
1237 
1238     DoTraceLevelMessage(
1239         This->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1240         "WDFWMIPROVIDER %p QueryAllData returning %!STATUS!, buffer used 0x%x",
1241         Provider->GetHandle(), status, bufferUsed);
1242 
1243 Done:
1244     status = This->CompleteWmiRequest(Irp, status, bufferUsed);
1245 
1246     return status;
1247 }
1248 
1249 _Must_inspect_result_
1250 NTSTATUS
_QuerySingleInstance(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1251 FxWmiIrpHandler::_QuerySingleInstance(
1252     __in FxWmiIrpHandler* This,
1253     __in PIRP Irp,
1254     __in FxWmiProvider* Provider,
1255     __in FxWmiInstance* Instance
1256     )
1257 {
1258     PWNODE_SINGLE_INSTANCE pSingle;
1259     PIO_STACK_LOCATION stack;
1260     NTSTATUS status;
1261     ULONG size, bufferSize;
1262 
1263     size = 0;
1264 
1265     stack = IoGetCurrentIrpStackLocation(Irp);
1266     pSingle = (PWNODE_SINGLE_INSTANCE) stack->Parameters.WMI.Buffer;
1267     bufferSize = stack->Parameters.WMI.BufferSize - pSingle->DataBlockOffset;
1268 
1269     if (Instance->IsQueryInstanceSupported() == FALSE) {
1270         status = STATUS_INVALID_DEVICE_REQUEST;
1271     }
1272     else if (bufferSize < Provider->GetMinInstanceBufferSize()) {
1273         size = Provider->GetMinInstanceBufferSize();
1274         status = STATUS_BUFFER_TOO_SMALL;
1275     }
1276     else {
1277         status = STATUS_SUCCESS;
1278     }
1279 
1280     if (NT_SUCCESS(status)) {
1281         status = Instance->QueryInstance(
1282             bufferSize,
1283             WDF_PTR_ADD_OFFSET(pSingle, pSingle->DataBlockOffset),
1284             &size
1285             );
1286 
1287         pSingle->SizeDataBlock = size;
1288     }
1289 
1290     status = This->CompleteWmiRequest(Irp, status, size);
1291 
1292     return status;
1293 }
1294 
1295 _Must_inspect_result_
1296 NTSTATUS
_ChangeSingleInstance(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1297 FxWmiIrpHandler::_ChangeSingleInstance(
1298     __in FxWmiIrpHandler* This,
1299     __in PIRP Irp,
1300     __in FxWmiProvider* Provider,
1301     __in FxWmiInstance* Instance
1302     )
1303 {
1304     PWNODE_SINGLE_INSTANCE pSingle;
1305     PIO_STACK_LOCATION stack;
1306     NTSTATUS status;
1307     ULONG size;
1308 
1309     size = 0;
1310 
1311     stack = IoGetCurrentIrpStackLocation(Irp);
1312     pSingle = (PWNODE_SINGLE_INSTANCE) stack->Parameters.WMI.Buffer;
1313 
1314     if (Instance->IsSetInstanceSupported() == FALSE) {
1315         status = STATUS_WMI_READ_ONLY;
1316     }
1317     else if (pSingle->SizeDataBlock < Provider->m_MinInstanceBufferSize) {
1318         size = Provider->m_MinInstanceBufferSize;
1319         status = STATUS_BUFFER_TOO_SMALL;
1320     }
1321     else {
1322         status = STATUS_SUCCESS;
1323     }
1324 
1325     if (NT_SUCCESS(status)) {
1326         size = pSingle->SizeDataBlock;
1327 
1328         status = Instance->SetInstance(
1329             pSingle->SizeDataBlock,
1330             size == 0 ?
1331                 NULL : WDF_PTR_ADD_OFFSET(pSingle, pSingle->DataBlockOffset)
1332             );
1333 
1334         ASSERT(status != STATUS_PENDING);
1335         if (status == STATUS_PENDING) {
1336             status = STATUS_UNSUCCESSFUL;
1337             size = 0;
1338         }
1339     }
1340 
1341     status = This->CompleteWmiRequest(Irp, status, size);
1342 
1343     return status;
1344 }
1345 
1346 _Must_inspect_result_
1347 NTSTATUS
_ChangeSingleItem(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1348 FxWmiIrpHandler::_ChangeSingleItem(
1349     __in FxWmiIrpHandler* This,
1350     __in PIRP Irp,
1351     __in FxWmiProvider* Provider,
1352     __in FxWmiInstance* Instance
1353     )
1354 {
1355     PWNODE_SINGLE_ITEM pSingle;
1356     NTSTATUS status;
1357     ULONG size;
1358 
1359     UNREFERENCED_PARAMETER(Provider);
1360 
1361     size = 0;
1362 
1363     pSingle = (PWNODE_SINGLE_ITEM) IoGetCurrentIrpStackLocation(Irp)->
1364         Parameters.WMI.Buffer;
1365 
1366     if (Instance->IsSetItemSupported() == FALSE) {
1367         status = STATUS_WMI_READ_ONLY;
1368     }
1369     else {
1370         status = STATUS_SUCCESS;
1371     }
1372 
1373     if (NT_SUCCESS(status)) {
1374         status = Instance->SetItem(
1375             pSingle->ItemId,
1376             pSingle->SizeDataItem,
1377             pSingle->SizeDataItem == 0 ?
1378                 NULL : WDF_PTR_ADD_OFFSET(pSingle, pSingle->DataBlockOffset)
1379             );
1380 
1381         ASSERT(status != STATUS_PENDING);
1382         if (status == STATUS_PENDING) {
1383             status = STATUS_UNSUCCESSFUL;
1384             size = 0;
1385         }
1386     }
1387 
1388     status = This->CompleteWmiRequest(Irp, status, size);
1389 
1390     return status;
1391 }
1392 
1393 _Must_inspect_result_
1394 NTSTATUS
_EnableDisableEventsAndCollection(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1395 FxWmiIrpHandler::_EnableDisableEventsAndCollection(
1396     __in FxWmiIrpHandler* This,
1397     __in PIRP Irp,
1398     __in FxWmiProvider* Provider,
1399     __in FxWmiInstance* Instance
1400     )
1401 {
1402     NTSTATUS status;
1403     BOOLEAN enable;
1404     WDF_WMI_PROVIDER_CONTROL control;
1405     PIO_STACK_LOCATION stack;
1406 
1407     UNREFERENCED_PARAMETER(This);
1408     UNREFERENCED_PARAMETER(Instance);
1409 
1410     Irp->IoStatus.Information = 0;
1411 
1412     stack = IoGetCurrentIrpStackLocation(Irp);
1413 
1414     if (stack->Parameters.WMI.BufferSize < sizeof(WNODE_HEADER)) {
1415         status = STATUS_INVALID_PARAMETER;
1416         goto Done;
1417     }
1418 
1419     switch (stack->MinorFunction) {
1420     case IRP_MN_ENABLE_EVENTS:
1421         enable = TRUE;
1422         control =  WdfWmiEventControl;
1423         break;
1424 
1425     case IRP_MN_DISABLE_EVENTS:
1426         enable = FALSE;
1427         control = WdfWmiEventControl;
1428         break;
1429 
1430     case IRP_MN_ENABLE_COLLECTION:
1431         enable = TRUE;
1432         control = WdfWmiInstanceControl;
1433         break;
1434 
1435     case IRP_MN_DISABLE_COLLECTION:
1436         enable = FALSE;
1437         control = WdfWmiInstanceControl;
1438         break;
1439 
1440     default:
1441         status = Irp->IoStatus.Status;
1442         goto Done;
1443     }
1444 
1445     if (control == WdfWmiEventControl) {
1446         Provider->m_EventControlEnabled = enable;
1447 
1448         //
1449         // Capture the tracing information before making the callback
1450         //
1451         if (Provider->m_Flags & WdfWmiProviderTracing) {
1452             Provider->SetTracingHandle(
1453                 ((PWNODE_HEADER) stack->Parameters.WMI.Buffer)->HistoricalContext
1454                 );
1455         }
1456     }
1457     else {
1458         Provider->m_DataBlockControlEnabled = enable;
1459     }
1460 
1461     if (Provider->IsFunctionControlSupported()) {
1462         status = Provider->FunctionControl(control, enable);
1463     }
1464     else {
1465         status = STATUS_SUCCESS;
1466     }
1467 
1468     ASSERT(status != STATUS_PENDING);
1469     if (status == STATUS_PENDING) {
1470         status = STATUS_UNSUCCESSFUL;
1471     }
1472 
1473     //
1474     // Undo the previous capture on error
1475     //
1476     if (!NT_SUCCESS(status)) {
1477         if (control == WdfWmiEventControl) {
1478             Provider->m_EventControlEnabled = FALSE;
1479 
1480             if (Provider->m_Flags & WdfWmiProviderTracing) {
1481                 Provider->SetTracingHandle(NULL);
1482             }
1483         }
1484         else {
1485             Provider->m_DataBlockControlEnabled = FALSE;
1486         }
1487 
1488     }
1489 
1490 Done:
1491     Irp->IoStatus.Status = status;
1492     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1493 
1494     return status;
1495 }
1496 
1497 _Must_inspect_result_
1498 NTSTATUS
_ExecuteMethod(__in FxWmiIrpHandler * This,__in PIRP Irp,__in FxWmiProvider * Provider,__in FxWmiInstance * Instance)1499 FxWmiIrpHandler::_ExecuteMethod(
1500     __in FxWmiIrpHandler* This,
1501     __in PIRP Irp,
1502     __in FxWmiProvider* Provider,
1503     __in FxWmiInstance* Instance
1504     )
1505 {
1506     PWNODE_METHOD_ITEM pMethod;
1507     PIO_STACK_LOCATION stack;
1508     NTSTATUS status;
1509     ULONG size, inBufferSize, outBufferSize;
1510 
1511     UNREFERENCED_PARAMETER(This);
1512     UNREFERENCED_PARAMETER(Provider);
1513 
1514     size = 0;
1515 
1516     stack = IoGetCurrentIrpStackLocation(Irp);
1517     pMethod = (PWNODE_METHOD_ITEM) stack->Parameters.WMI.Buffer;
1518     inBufferSize = pMethod->SizeDataBlock;
1519     outBufferSize = stack->Parameters.WMI.BufferSize - pMethod->DataBlockOffset;
1520 
1521 
1522 
1523 
1524 
1525 
1526 
1527 
1528 
1529 
1530 
1531     if (Instance->IsExecuteMethodSupported() == FALSE) {
1532         //
1533         // WmiLib returns this value when there is no execute method function
1534         // pointer specified to it.
1535         //
1536         status = STATUS_INVALID_DEVICE_REQUEST;
1537     }
1538     else {
1539         status = STATUS_SUCCESS;
1540     }
1541 
1542     if (NT_SUCCESS(status)) {
1543         status = Instance->ExecuteMethod(
1544             pMethod->MethodId,
1545             inBufferSize,
1546             outBufferSize,
1547             (inBufferSize == 0 && outBufferSize == 0) ?
1548                 NULL : WDF_PTR_ADD_OFFSET(pMethod, pMethod->DataBlockOffset),
1549             &size
1550             );
1551 
1552         ASSERT(status != STATUS_PENDING);
1553         if (status == STATUS_PENDING) {
1554             status = STATUS_UNSUCCESSFUL;
1555             size = 0;
1556         }
1557     }
1558 
1559     status = This->CompleteWmiRequest(Irp, status, size);
1560 
1561     return status;
1562 }
1563 
1564 _Must_inspect_result_
1565 NTSTATUS
_RegInfo(__in FxWmiIrpHandler * This,__in PIRP Irp,__in_opt FxWmiProvider * Provider,__in_opt FxWmiInstance * Instance)1566 FxWmiIrpHandler::_RegInfo(
1567     __in FxWmiIrpHandler* This,
1568     __in PIRP Irp,
1569     __in_opt FxWmiProvider* Provider,
1570     __in_opt FxWmiInstance* Instance
1571     )
1572 {
1573     PIO_STACK_LOCATION stack;
1574     PUNICODE_STRING pRegPath;
1575     PWMIREGINFO pWmiRegInfo;
1576     PUCHAR pBuffer;
1577     PUNICODE_STRING pMofString;
1578     FxDevice* pDevice;
1579     NTSTATUS status;
1580     ULONG registryPathOffset, mofResourceOffset, bufferNeeded, information,
1581           bufferSize;
1582     KIRQL irql;
1583 
1584     UNREFERENCED_PARAMETER(Provider);
1585     UNREFERENCED_PARAMETER(Instance);
1586 
1587     information = 0;
1588 
1589     stack = IoGetCurrentIrpStackLocation(Irp);
1590     pBuffer = (PUCHAR) stack->Parameters.WMI.Buffer;
1591     bufferSize = stack->Parameters.WMI.BufferSize;
1592 
1593     pDevice = This->m_Device;
1594 
1595     This->Lock(&irql);
1596 
1597     mofResourceOffset = sizeof(WMIREGINFO) +
1598                         This->m_NumProviders * sizeof(WMIREGGUIDW);
1599 
1600     pMofString = NULL;
1601 
1602     if (pDevice->m_MofResourceName.Buffer != NULL) {
1603         pMofString = &pDevice->m_MofResourceName;
1604     }
1605     else {
1606         //
1607         // Start with the parent and iterate up until we hit a device without
1608         // a parent.
1609         //
1610         pDevice = pDevice->m_ParentDevice;
1611 
1612         while (pDevice != NULL) {
1613             if (pDevice->m_MofResourceName.Buffer != NULL) {
1614                 pMofString = &pDevice->m_MofResourceName;
1615                 break;
1616             }
1617 
1618             //
1619             // Advance to the next ancestor
1620             //
1621             pDevice = pDevice->m_ParentDevice;
1622         }
1623 
1624         //
1625         // Restore pDevice back to this device
1626         //
1627         pDevice = This->m_Device;
1628     }
1629 
1630     pRegPath = pDevice->GetDriver()->GetRegistryPathUnicodeString();
1631 
1632     //
1633     // if there is a mof string, add its length.  We always need to at least
1634     // add the USHORT to indicate a string of size 0.
1635     //
1636     registryPathOffset = mofResourceOffset + sizeof(USHORT);
1637     if (pMofString != NULL) {
1638         registryPathOffset += pMofString->Length;
1639     }
1640 
1641     //
1642     // eventually bufferNeeded = registryPathOffset + pRegPath->Length +
1643     //                           sizeof(USHORT)
1644     //
1645     status = RtlULongAdd(registryPathOffset, pRegPath->Length, &bufferNeeded);
1646     if (!NT_SUCCESS(status)) {
1647         goto Done;
1648     }
1649 
1650     status = RtlULongAdd(bufferNeeded, sizeof(USHORT), &bufferNeeded);
1651     if (!NT_SUCCESS(status)) {
1652         goto Done;
1653     }
1654 
1655     if (bufferNeeded <= bufferSize) {
1656         PLIST_ENTRY ple;
1657         ULONG i;
1658         BOOLEAN addref;
1659 
1660         information = bufferNeeded;
1661 
1662         pWmiRegInfo = (PWMIREGINFO) pBuffer;
1663         pWmiRegInfo->BufferSize = bufferNeeded;
1664         pWmiRegInfo->NextWmiRegInfo = 0;
1665         pWmiRegInfo->MofResourceName = mofResourceOffset;
1666         pWmiRegInfo->RegistryPath = registryPathOffset;
1667         pWmiRegInfo->GuidCount = This->m_NumProviders;
1668 
1669         addref = IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_REGINFO_EX
1670             ? TRUE : FALSE;
1671 
1672         for (ple = This->m_ProvidersListHead.Flink, i = 0;
1673              i < This->m_NumProviders;
1674              ple = ple->Flink, i++) {
1675 
1676             PWMIREGGUIDW pWmiRegGuid;
1677             PDEVICE_OBJECT pdo;
1678             FxWmiProvider* pProvider;
1679 
1680             pProvider = CONTAINING_RECORD(ple, FxWmiProvider, m_ListEntry);
1681 
1682             pWmiRegGuid = &pWmiRegInfo->WmiRegGuid[i];
1683 
1684             RtlCopyMemory(&pWmiRegGuid->Guid,
1685                           &pProvider->m_Guid,
1686                           sizeof(GUID));
1687 
1688             pWmiRegGuid->InstanceCount = pProvider->m_NumInstances;
1689 
1690             pWmiRegGuid->Flags = pProvider->GetRegistrationFlagsLocked();
1691 
1692             pdo = pDevice->GetPhysicalDevice();
1693             pWmiRegGuid->Pdo = (ULONG_PTR) pdo;
1694 
1695             if (addref) {
1696                 ObReferenceObject(pdo);
1697             }
1698         }
1699     }
1700     else {
1701         *((PULONG) pBuffer) = bufferNeeded;
1702         information = sizeof(ULONG);
1703     }
1704 
1705     This->Unlock(irql);
1706 
1707     //
1708     // Must copy the strings outside of any lock since they are in paged pool
1709     //
1710     if (bufferNeeded <= bufferSize) {
1711         PUSHORT pLength;
1712 
1713         pLength = WDF_PTR_ADD_OFFSET_TYPE(pBuffer, mofResourceOffset, PUSHORT);
1714 
1715         if (pMofString != NULL) {
1716             *pLength = pMofString->Length;
1717 
1718             RtlCopyMemory(pLength + 1,
1719                           pMofString->Buffer,
1720                           pMofString->Length);
1721         }
1722         else {
1723             *pLength = 0;
1724         }
1725 
1726         pLength = WDF_PTR_ADD_OFFSET_TYPE(pBuffer, registryPathOffset, PUSHORT);
1727 
1728         *pLength = pRegPath->Length;
1729         RtlCopyMemory(pLength + 1,
1730                       pRegPath->Buffer,
1731                       pRegPath->Length);
1732     }
1733 
1734     status = STATUS_SUCCESS;
1735 
1736 Done:
1737     if (!NT_SUCCESS(status)) {
1738         This->Unlock(irql);
1739     }
1740 
1741     Irp->IoStatus.Status = status;
1742     Irp->IoStatus.Information = information;
1743 
1744     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1745 
1746     return status;
1747 }
1748 
1749 VOID
CompleteWmiQueryAllDataRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1750 FxWmiIrpHandler::CompleteWmiQueryAllDataRequest(
1751     __in PIRP Irp,
1752     __in NTSTATUS Status,
1753     __in ULONG BufferUsed
1754     )
1755 {
1756     PWNODE_ALL_DATA pNodeAllData;
1757     ULONG bufferNeeded, information;
1758     PIO_STACK_LOCATION stack;
1759 
1760     stack = IoGetCurrentIrpStackLocation(Irp);
1761 
1762     pNodeAllData = (PWNODE_ALL_DATA) stack->Parameters.WMI.Buffer;
1763     bufferNeeded = pNodeAllData->DataBlockOffset + BufferUsed;
1764 
1765     if (NT_SUCCESS(Status) && bufferNeeded > stack->Parameters.WMI.BufferSize) {
1766         Status = STATUS_BUFFER_TOO_SMALL;
1767     }
1768 
1769     if (NT_SUCCESS(Status)) {
1770         KeQuerySystemTime(&pNodeAllData->WnodeHeader.TimeStamp);
1771 
1772         pNodeAllData->WnodeHeader.BufferSize = bufferNeeded;
1773         information = bufferNeeded;
1774     }
1775     else if (Status == STATUS_BUFFER_TOO_SMALL) {
1776         PWNODE_TOO_SMALL pNodeTooSmall;
1777 
1778         pNodeTooSmall = (PWNODE_TOO_SMALL) stack->Parameters.WMI.Buffer;
1779 
1780         pNodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
1781         pNodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
1782         pNodeTooSmall->SizeNeeded = bufferNeeded;
1783 
1784         information = sizeof(WNODE_TOO_SMALL);
1785         Status = STATUS_SUCCESS;
1786     }
1787     else {
1788         information = 0;
1789     }
1790 
1791     Irp->IoStatus.Information = information;
1792     Irp->IoStatus.Status = Status;
1793 }
1794 
1795 VOID
CompleteWmiQuerySingleInstanceRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1796 FxWmiIrpHandler::CompleteWmiQuerySingleInstanceRequest(
1797     __in PIRP Irp,
1798     __in NTSTATUS Status,
1799     __in ULONG BufferUsed
1800     )
1801 {
1802     PWNODE_SINGLE_INSTANCE pNodeSingle;
1803     ULONG bufferNeeded, information;
1804 
1805     pNodeSingle = (PWNODE_SINGLE_INSTANCE)
1806         IoGetCurrentIrpStackLocation(Irp)->Parameters.WMI.Buffer;
1807 
1808     bufferNeeded = pNodeSingle->DataBlockOffset + BufferUsed;
1809 
1810     if (NT_SUCCESS(Status)) {
1811         pNodeSingle->WnodeHeader.BufferSize = bufferNeeded;
1812         KeQuerySystemTime(&pNodeSingle->WnodeHeader.TimeStamp);
1813 
1814         ASSERT(pNodeSingle->SizeDataBlock <= BufferUsed);
1815         information = bufferNeeded;
1816     }
1817     else if (Status == STATUS_BUFFER_TOO_SMALL) {
1818         PWNODE_TOO_SMALL pNodeTooSmall;
1819 
1820         pNodeTooSmall = (PWNODE_TOO_SMALL) pNodeSingle;
1821 
1822         pNodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
1823         pNodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
1824         pNodeTooSmall->SizeNeeded = bufferNeeded;
1825 
1826         information = sizeof(WNODE_TOO_SMALL);
1827         Status = STATUS_SUCCESS;
1828     }
1829     else {
1830         information = 0;
1831     }
1832 
1833     Irp->IoStatus.Information = information;
1834     Irp->IoStatus.Status = Status;
1835 }
1836 
1837 VOID
CompleteWmiExecuteMethodRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1838 FxWmiIrpHandler::CompleteWmiExecuteMethodRequest(
1839     __in PIRP Irp,
1840     __in NTSTATUS Status,
1841     __in ULONG BufferUsed
1842     )
1843 {
1844     PWNODE_METHOD_ITEM pNodeMethod;
1845     ULONG bufferNeeded, information;
1846 
1847     pNodeMethod = (PWNODE_METHOD_ITEM)
1848         IoGetCurrentIrpStackLocation(Irp)->Parameters.WMI.Buffer;
1849 
1850     bufferNeeded = pNodeMethod->DataBlockOffset + BufferUsed;
1851 
1852     if (NT_SUCCESS(Status)) {
1853         pNodeMethod->WnodeHeader.BufferSize = bufferNeeded;
1854         pNodeMethod->SizeDataBlock = BufferUsed;
1855         KeQuerySystemTime(&pNodeMethod->WnodeHeader.TimeStamp);
1856 
1857         information = bufferNeeded;
1858     }
1859     else if (Status == STATUS_BUFFER_TOO_SMALL) {
1860         PWNODE_TOO_SMALL pNodeTooSmall;
1861 
1862         pNodeTooSmall = (PWNODE_TOO_SMALL) pNodeMethod;
1863 
1864         pNodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
1865         pNodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
1866         pNodeTooSmall->SizeNeeded = bufferNeeded;
1867 
1868         information = sizeof(WNODE_TOO_SMALL);
1869         Status = STATUS_SUCCESS;
1870     }
1871     else {
1872         information = 0;
1873     }
1874 
1875     Irp->IoStatus.Information = information;
1876     Irp->IoStatus.Status = Status;
1877 }
1878 
1879 _Must_inspect_result_
1880 NTSTATUS
CompleteWmiRequest(__in PIRP Irp,__in NTSTATUS Status,__in ULONG BufferUsed)1881 FxWmiIrpHandler::CompleteWmiRequest(
1882     __in PIRP Irp,
1883     __in NTSTATUS Status,
1884     __in ULONG BufferUsed
1885     )
1886 /*++
1887 
1888 Routine Description:
1889     This routine will do the work of completing a WMI irp. Depending upon the
1890     the WMI request this routine will fixup the returned WNODE appropriately.
1891 
1892     This may be called at DPC level
1893 
1894 Arguments:
1895 
1896     Irp - Supplies the Irp making the request.
1897 
1898     Status has the return status code for the IRP
1899 
1900     BufferUsed has the number of bytes needed by the device to return the
1901        data requested in any query. In the case that the buffer passed to
1902        the device is too small this has the number of bytes needed for the
1903        return data. If the buffer passed is large enough then this has the
1904        number of bytes actually used by the device.
1905 
1906 Return Value:
1907 
1908     status
1909 
1910 --*/
1911 {
1912     switch (IoGetCurrentIrpStackLocation(Irp)->MinorFunction) {
1913     case IRP_MN_QUERY_ALL_DATA:
1914         CompleteWmiQueryAllDataRequest(Irp, Status, BufferUsed);
1915         break;
1916 
1917     case IRP_MN_QUERY_SINGLE_INSTANCE:
1918         CompleteWmiQuerySingleInstanceRequest(Irp, Status, BufferUsed);
1919         break;
1920 
1921     case IRP_MN_EXECUTE_METHOD:
1922         CompleteWmiExecuteMethodRequest(Irp, Status, BufferUsed);
1923         break;
1924 
1925     case IRP_MN_CHANGE_SINGLE_INSTANCE:
1926     case IRP_MN_CHANGE_SINGLE_ITEM:
1927         //
1928         // Lot's of drivers return STATUS_BUFFER_TOO_SMALL for an invalid buffer
1929         // size.  WMI expects STATUS_WMI_SET_FAILURE in this case.  Change the
1930         // Status to the expected value.  No way to return any size in
1931         // Information, the buffer is input only.
1932         //
1933         if (Status == STATUS_BUFFER_TOO_SMALL) {
1934             DoTraceLevelMessage(
1935                 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1936                 "Converting %!STATUS! to %!STATUS!",
1937                 Status, STATUS_WMI_SET_FAILURE);
1938             Status = STATUS_WMI_SET_FAILURE;
1939         }
1940         //  ||   ||  Fall through  ||   ||
1941         //  \/   \/                \/   \/
1942 
1943     default:
1944         //
1945         // All other requests don't return any data
1946         //
1947         Irp->IoStatus.Status = Status;
1948         Irp->IoStatus.Information = 0;
1949         break;
1950     }
1951 
1952     //
1953     // One of the complete functions may have morphed the status value
1954     //
1955     Status = Irp->IoStatus.Status;
1956     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1957 
1958     return Status;
1959 }
1960 
1961