1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxObject.cpp
8 
9 Abstract:
10 
11     This module contains the implementation of the base object
12 
13 Author:
14 
15 
16 
17 
18 
19 Environment:
20 
21     Both kernel and user mode
22 
23 Revision History:
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 --*/
35 
36 #include "fxobjectpch.hpp"
37 
38 extern "C" {
39 
40 #if defined(EVENT_TRACING)
41 #include "FxObject.tmh"
42 #else
43 ULONG DebugLevel = TRACE_LEVEL_INFORMATION;
44 ULONG DebugFlag = 0xff;
45 #endif
46 
47 }
48 
49 FxObject::FxObject(
50     __in WDFTYPE Type,
51     __in USHORT Size,
52     __in PFX_DRIVER_GLOBALS FxDriverGlobals
53     ) :
54     m_Type(Type),
55     m_ObjectSize((USHORT) WDF_ALIGN_SIZE_UP(Size, MEMORY_ALLOCATION_ALIGNMENT)),
56     m_Globals(FxDriverGlobals)
57 #if FX_CORE_MODE==FX_CORE_USER_MODE
58 #ifndef INLINE_WRAPPER_ALLOCATION
59     ,m_COMWrapper(NULL)
60 #endif
61 #endif
62 {
63     ASSERT((((ULONG_PTR) this) & FxHandleFlagMask) == 0x0);
64 
65     Construct(FALSE);
66 }
67 
68 FxObject::FxObject(
69     __in WDFTYPE Type,
70     __in USHORT Size,
71     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
72     __in FxObjectType ObjectType
73     ) :
74     m_Type(Type),
75     m_ObjectSize((USHORT) WDF_ALIGN_SIZE_UP(Size, MEMORY_ALLOCATION_ALIGNMENT)),
76     m_Globals(FxDriverGlobals)
77 {
78      if (ObjectType != FxObjectTypeEmbedded) {
79          //
80          // only for non embedded objects
81          //
82          ASSERT((((ULONG_PTR) this) & FxHandleFlagMask) == 0x0);
83      }
84 
85     Construct(ObjectType == FxObjectTypeEmbedded ? TRUE : FALSE);
86 }
87 
88 FxObject::~FxObject()
89 {
90     FxTagTracker *pTagTracker;
91 
92 
93 
94 
95 
96 
97 
98     pTagTracker = GetTagTracker();
99 
100     if (pTagTracker != NULL) {
101         delete pTagTracker;
102     }
103 
104     ASSERT(m_DisposeSingleEntry.Next == NULL);
105 
106     //
107     // We need to ensure there are no leaked child objects, or
108     // parent associations.
109     //
110     // This can occur if someone calls the C++ operator delete,
111     // or Release() to destroy the object.
112     //
113     // This is generally invalid in the framework, though certain
114     // objects may understand the underlying contract, but must
115     // make sure there are no left over un-Disposed associations.
116     //
117     // Embedded FxObject's also don't have delete called on them,
118     // and have to manually manage any child associations when
119     // their parent object disposes by calling PerformEarlyDispose.
120     //
121     // They don't have an associated lifetime parent since they
122     // are embedded.
123     //
124     if (m_ParentObject != NULL ||
125         !IsListEmpty(&m_ChildListHead) || !IsListEmpty(&m_ChildEntry)) {
126         PCSTR pHandleName;
127 
128         pHandleName = FxObjectTypeToHandleName(m_Type);
129         if (pHandleName == NULL) {
130             pHandleName = "WDFOBJECT";
131         }
132 
133         ASSERTMSG(
134             "Object was freed using WdfObjectDereference, not WdfObjectDelete\n",
135             m_ParentObject == NULL &&
136             IsListEmpty(&m_ChildEntry) &&
137             IsListEmpty(&m_ChildListHead)
138             );
139 
140         DoTraceLevelMessage(
141             GetDriverGlobals(), TRACE_LEVEL_FATAL, TRACINGOBJECT,
142             "Handle %s %p (raw object %p) was freed using "
143             "WdfObjectDereference(), not WdfObjectDelete()",
144             pHandleName, GetObjectHandleUnchecked(), this);
145 
146         FxVerifierBugCheck(GetDriverGlobals(),
147                            WDF_OBJECT_ERROR,
148                            (ULONG_PTR) GetObjectHandleUnchecked(),
149                            (ULONG_PTR) this);
150     }
151 
152     //
153     // This is called when the reference count goes to zero
154     //
155     SetObjectStateLocked(FxObjectStateDestroyed);
156 }
157 
158 
159 VOID
160 FX_VF_METHOD(FxObject, VerifyConstruct) (
161     _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
162     _In_ BOOLEAN Embedded
163     )
164 {
165     UNREFERENCED_PARAMETER(FxDriverGlobals);
166 
167 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
168     PAGED_CODE_LOCKED();
169 #endif
170 
171     ASSERTMSG(
172         "this object's type is not listed in FxObjectsInfo\n",
173         FxVerifyObjectTypeInTable(m_Type));
174 
175     //
176     // If this is an embedded object, there is no possibility of having
177     // a debug extension, so do not set FXOBJECT_FLAGS_HAS_DEBUG *ever*
178     // in this case.
179     //
180     if (m_Globals->IsObjectDebugOn() && Embedded == FALSE) {
181         FxObjectDebugExtension* pExtension;
182 
183         m_ObjectFlags |= FXOBJECT_FLAGS_HAS_DEBUG;
184 
185         pExtension = GetDebugExtension();
186         ASSERT(pExtension->Signature == FxObjectDebugExtensionSignature);
187 
188         //
189         // Assume that zero is an invalid state.
190         //
191         WDFCASSERT(FxObjectStateInvalid == 0x0);
192         RtlZeroMemory(&pExtension->StateHistory[0],
193                       ARRAY_SIZE(pExtension->StateHistory));
194         pExtension->StateHistoryIndex = 0;
195 
196         //
197         // Setup the first slot to our new state.
198         //
199         pExtension->StateHistory[0] = FxObjectStateCreated;
200 
201         AllocateTagTracker(m_Type);
202     }
203 }
204 
205 
206 VOID
207 FxObject::FinalRelease(
208     VOID
209     )
210 {
211 
212 
213 
214 
215 
216 
217 
218 
219 
220 
221 
222 
223     //
224     // No other access, OK to test flag without grabbing spinlock
225     // since it can only be set at create.
226     //
227     if (ShouldDeferDisposeLocked()) {
228         //
229         // If this is a passive level only object, ensure we only destroy
230         // it from passive level.  No need to hold the object lock when
231         // changing to this state since no other thread can change our
232         // state.
233         //
234         SetObjectStateLocked(FxObjectStateDeferedDestroy);
235 
236         //
237         // Note, we cannot be holding a lock while making this call b/c by
238         // the time it returns, it could have freed the object and the
239         // KeReleaseSpinLock call would have touched freed pool.
240         //
241 
242         //FxToObjectItf::FxAddToDriverDisposeList(m_Globals, this);
243 
244 
245         m_Globals->Driver->GetDisposeList()->Add(this);
246 
247     }
248     else {
249         ProcessDestroy();
250     }
251 }
252 
253 _Must_inspect_result_
254 NTSTATUS
255 FxObject::QueryInterface(
256     __in FxQueryInterfaceParams* Params
257     )
258 {
259     NTSTATUS status;
260 
261     if (Params->Type == FX_TYPE_OBJECT) {
262         *Params->Object = this;
263         status = STATUS_SUCCESS;
264     }
265     else {
266         status = STATUS_NOINTERFACE;
267     }
268 
269     return status;
270 }
271 
272 VOID
273 FxObject::AllocateTagTracker(
274     __in WDFTYPE Type
275     )
276 {
277     ASSERT(IsDebug());
278 
279     if (m_Globals->DebugExtension != NULL &&
280         m_Globals->DebugExtension->ObjectDebugInfo != NULL &&
281         FxVerifierGetTrackReferences(
282             m_Globals->DebugExtension->ObjectDebugInfo,
283             Type)) {
284         //
285         // Failure to CreateAndInitialize a tag tracker is no big deal, we just
286         // don't track references.
287         //
288 
289         (void) FxTagTracker::CreateAndInitialize(
290             &GetDebugExtension()->TagTracker,
291             m_Globals,
292             FxTagTrackerTypeHandle,
293             FALSE,
294             this
295             );
296 
297         //
298         // For now we overload the requirement of a tag tracker as also tracing
299         // state changes.
300         //
301         m_ObjectFlags |= FXOBJECT_FLAGS_TRACE_STATE;
302     }
303 }
304 
305 VOID
306 FxObject::operator delete(
307     __in PVOID Memory
308     )
309 {
310     ASSERT(Memory != NULL);
311 
312 
313 
314 
315 
316 
317 
318 
319 
320 
321 
322 
323 
324     FxPoolFree(_GetBase((FxObject*) Memory));
325 }
326 
327 VOID
328 FxObject::CallCleanupCallbacks(
329     VOID
330     )
331 {
332     FxContextHeader* pHeader;
333     WDFOBJECT h;
334 
335     //
336     // Deleted before Commit or it is an internal object
337     //
338     if (IsCommitted() == FALSE) {
339         return;
340     }
341 
342     //
343     // We only should have an object handle when we have an external object
344     //
345     h = GetObjectHandle();
346 
347     for (pHeader = GetContextHeader();
348          pHeader != NULL;
349          pHeader = pHeader->NextHeader) {
350         if (pHeader->EvtCleanupCallback != NULL) {
351             pHeader->EvtCleanupCallback(h);
352             pHeader->EvtCleanupCallback = NULL;
353         }
354     }
355 
356     m_ObjectFlags &= ~FXOBJECT_FLAGS_HAS_CLEANUP;
357 }
358 
359 VOID
360 FxObject::ClearEvtCallbacks(
361     VOID
362     )
363 /*++
364 
365 Routine Description:
366 
367     Clears out any assigned callbacks on the object.
368 
369 Arguments:
370     None
371 
372 Return Value:
373     None
374 
375   --*/
376 {
377     FxContextHeader *pHeader;
378 
379     for (pHeader = GetContextHeader();
380          pHeader != NULL;
381          pHeader = pHeader->NextHeader) {
382 
383         pHeader->EvtDestroyCallback = NULL;
384         pHeader->EvtCleanupCallback = NULL;
385     }
386 
387     m_ObjectFlags &= ~FXOBJECT_FLAGS_HAS_CLEANUP;
388 }
389 
390 VOID
391 FxObject::DeleteFromFailedCreate(
392     VOID
393     )
394 /*++
395 
396 Routine Description:
397     Clears out any assigned callbacks on the object and then deletes it.  Clearing
398     out the callbacks are necessary so that the driver's callbacks are not called
399     on a buffer that they didn't initialize.
400 
401 Arguments:
402     None
403 
404 Return Value:
405     None
406 
407   --*/
408 {
409     ClearEvtCallbacks();
410 
411     //
412     // After this call returns "this" is destroyed is not a valid pointer!
413     //
414     DeleteObject();
415 
416     //
417     // "this" is now freed memory, do not touch it!
418     //
419 }
420 
421 //
422 // FxObject Parent/Child Rules:
423 //
424 // The FxObject state machine protects state transitions when
425 // objects are being associated with each other, and races
426 // that can occur when a child and parent object are being
427 // deleted at the same time. This provides the backbone of
428 // assurance on the objects state.
429 //
430 // While transiting object states, the following must be taken
431 // into consideration:
432 //
433 // Reference counts:
434 //
435 //  When an object is created with operator new(), it has a reference count
436 //  of one.
437 //
438 //  When a DeleteObject is done on it, the reference count is decremented
439 //  after delete processing is done (which can include disposing children),
440 //  and this results in the objects destruction.
441 //
442 //  When an object is created by operator new() and immediately associated
443 //  with a parent object, its reference count remains one.
444 //
445 //  When either the DeleteObject() method is invoked, or the parent object
446 //  disposes the child object with ParentDeleteEvent(), eventually the
447 //  object will dereference itself after delete processing is done only
448 //  once.
449 //
450 //  Even in the face of race conditions, the object state machine ensures that
451 //  only one set of the proper delete conditions occur (dispose children, invoke
452 //  driver and class dispose callbacks, dereference object).
453 //
454 //  An object is responsible for releasing its own reference count on its
455 //  self when it goes into the FxObjectStateDeletedAndDisposed.
456 //
457 //  This has been *carefully* designed so that only the original object
458 //  references are required for this automatic lifetime case to avoid
459 //  extra interlocked operations in AddRef and Release frequently. These
460 //  extra interlocked operations can have a big performance impact on
461 //  the WDF main I/O paths, in which object relationship's are being used.
462 //
463 //  A simpler implementation may try and have the parent add a reference
464 //  to the child, and the child add a reference to the parent, but this
465 //  is a tightly coupled implementation controlled by the object state
466 //  machine. A circular reference pattern is OK for objects that
467 //  do not have a formal designed in relationship that can be expressed
468 //  in the complex tear down contract implemented in the state machine.
469 //
470 // SpinLocks:
471 //
472 // The m_SpinLock field protects each indivdual objects m_ObjectState
473 // variable, the list of child objects that it has, and the m_ParentObject
474 // field.
475 //
476 // In addition, if any object associates itself with a parent object,
477 // it effectively "lends" its m_ChildEntry field to the parent object,
478 // and these are manipulated and protected by the parent objects m_SpinLock.
479 //
480 // Lock Order:
481 //
482 // Currently the lock order is Child -> Parent, meaning a child
483 // can call the AddChildObjectInternal, RemoveChildObjectInternal, methods with
484 // the child lock held.
485 //
486 // The parent object will not invoke any child object ParentDeleteEvent()
487 // while holding the parents m_SpinLock.
488 //
489 // This order allows potential races between child DeleteObject and parent
490 // Dispose to be resolved without extra reference counts and multiple
491 // acquires and releases of the spinlocks in the normal cases.
492 //
493 //
494 // AddChildObjectInternal/RemoveChildObjectInternal:
495 //
496 // When a child object is added to the parent, the parent can delete
497 // the child object at any time when the parent object is deleted
498 // or disposed.
499 //
500 // If a call to RemoveChildObjectInternal is made, the caller may not "win"
501 // the inherent race with a parent object Dispose() occuring. This
502 // is similar to the WDM IRP cancel race, and is handled in exactly
503 // the same fashion.
504 //
505 // If an object is associated with a parent, and later removal
506 // is desired, the return status of RemoveChildObjectInternal must be
507 // tested, and if not success, the caller should not delete the
508 // child object itself, since the parent is in the process
509 // of doing so. The caller "lost the Dispose race".
510 //
511 //
512 // m_ParentObject field:
513 //
514 // This field is set by the child object after it successfully
515 // adds a parent object, and clear it when it is disposed by
516 // the parent, or removes the parent association.
517 //
518 // It is protected by the childs m_SpinLock field.
519 //
520 
521 
522 
523 
524 
525 
526 
527 _Must_inspect_result_
528 NTSTATUS
529 FxObject::AssignParentObject(
530     __in FxObject* ParentObject
531     )
532 /*++
533 
534 Routine Description:
535     Assign a parent to the current object.      The parent can not be the same
536     object.
537 
538 Arguments:
539     ParentObject - Object to become the parent for this object
540 
541 Returns:
542 
543 Comments:
544 
545     The caller "passes" its initial object reference to us, so we
546     do not take an additional reference on the object.
547 
548     If we are deleted, Dispose() will be invoked on the object, and
549     its reference will be released.
550 
551     This provides automatic deletion of associated child objects if
552     the object does not keep any extra references.
553 
554 --*/
555 {
556     KIRQL oldIrql;
557     NTSTATUS status;
558 
559     m_SpinLock.Acquire(&oldIrql);
560 
561     //
562     // Can't add a parent if the current object is being deleted
563     //
564     if (m_ObjectState != FxObjectStateCreated) {
565         TraceDroppedEvent(FxObjectDroppedEventAssignParentObject);
566         m_SpinLock.Release(oldIrql);
567         return STATUS_DELETE_PENDING;
568     }
569 
570     //
571     // Current Object can't already have a parent, and can't
572     // be its own parent.
573     //
574     if (m_ParentObject != NULL) {
575         m_SpinLock.Release(oldIrql);
576         return STATUS_WDF_PARENT_ALREADY_ASSIGNED;
577     }
578 
579     if (m_ParentObject == this) {
580         m_SpinLock.Release(oldIrql);
581         return STATUS_WDF_PARENT_IS_SELF;
582     }
583 
584     //
585     // We don't allow a parent object to be assigned after
586     // FxObject::Commit().
587     //
588     ASSERTMSG("Parent object can not be assigned after Commit()\n", !IsCommitted());
589 
590     ASSERT(IsListEmpty(&this->m_ChildEntry));
591 
592     status = ParentObject->AddChildObjectInternal(this);
593 
594     if (NT_SUCCESS(status)) {
595         m_ParentObject = ParentObject;
596     }
597 
598     m_SpinLock.Release(oldIrql);
599 
600     return status;
601 }
602 
603 _Must_inspect_result_
604 NTSTATUS
605 FxObject::AddContext(
606     __in FxContextHeader *Header,
607     __in PVOID* Context,
608     __in PWDF_OBJECT_ATTRIBUTES Attributes
609     )
610 {
611     FxContextHeader *pCur, **ppLast;
612     NTSTATUS status;
613     KIRQL irql;
614 
615     status = STATUS_UNSUCCESSFUL;
616 
617     pCur = GetContextHeader();
618 
619     //
620     // This should never happen since all outward facing objects have a
621     // context header; framework never calls this function on internal
622     // objects.
623     //
624     ASSERT(pCur != NULL);
625 
626     //
627     // Acquire the lock to lock the object's state.  A side affect of grabbing
628     // the lock is that all updaters who want to add a context are serialized.
629     // All callers who want to find a context do not need to acquire the lock
630     // becuase they are not going to update the list, just read from it.
631     //
632     // Once a context has been added, it will not be removed until the object
633     // has been deleted.
634     //
635     m_SpinLock.Acquire(&irql);
636 
637     if (m_ObjectState == FxObjectStateCreated && pCur != NULL) {
638         //
639         // Iterate over the list of contexts already on this object and see if
640         // this type already is attached.
641         //
642         for (ppLast = &pCur->NextHeader;
643              pCur != NULL;
644              ppLast = &pCur->NextHeader, pCur = pCur->NextHeader) {
645 
646             if (pCur->ContextTypeInfo == Header->ContextTypeInfo) {
647                 //
648                 // Dupe found, return error but give the caller the context
649                 // pointer
650                 //
651                 if (Context != NULL) {
652                     *Context = &pCur->Context[0];
653                 }
654 
655                 status = STATUS_OBJECT_NAME_EXISTS;
656                 break;
657             }
658         }
659 
660         if (pCur == NULL) {
661             //
662             // By using the interlocked to update, we don't need to use a lock
663             // when walking the list to find the context.   The only reason
664             // we are holding the object lock is to lock the current state
665             // (m_ObjectState) of the object.
666             //
667             InterlockedExchangePointer((PVOID*) ppLast, Header);
668             status = STATUS_SUCCESS;
669 
670             if (Context != NULL) {
671                 *Context = &Header->Context[0];
672             }
673 
674             //
675             // FxContextHeaderInit does not set these callbacks.  If this were
676             // the creation of the object itself, FxObject::Commit would have done
677             // this assignment.
678             //
679             Header->EvtDestroyCallback = Attributes->EvtDestroyCallback;
680 
681             if (Attributes->EvtCleanupCallback != NULL) {
682                 Header->EvtCleanupCallback = Attributes->EvtCleanupCallback;
683                 m_ObjectFlags |= FXOBJECT_FLAGS_HAS_CLEANUP;
684             }
685 
686         }
687     }
688     else {
689         //
690         // Object is being torn down, adding a context is a bad idea because we
691         // cannot guarantee that the cleanup or destroy routines will be called
692         //
693         status = STATUS_DELETE_PENDING;
694     }
695 
696     m_SpinLock.Release(irql);
697 
698     return status;
699 }
700 
701 _Must_inspect_result_
702 NTSTATUS
703 FxObject::AddChildObjectInternal(
704     __in FxObject* ChildObject
705     )
706 
707 /*++
708 
709 Routine Description:
710     Called by an object to be added to this objects child list
711 
712 Arguments:
713     ChildObject - Object to add this this objects child list
714 
715 Returns:
716     NTSTATUS
717 
718 Comments:
719     The caller "passes" its initial object reference to us, so we
720     do not take an additional reference on the object.
721 
722     If we are deleted, Dispose() will be invoked on the object, and
723     its reference will be released.
724 
725     This provides automatic deletion of associated child objects if
726     the object does not keep any extra references.
727 
728 --*/
729 {
730     KIRQL oldIrql;
731 
732     m_SpinLock.Acquire(&oldIrql);
733 
734     //
735     // Can't add child if the current object is being deleted
736     //
737     if (m_ObjectState != FxObjectStateCreated) {
738         TraceDroppedEvent(FxObjectDroppedEventAddChildObjectInternal);
739         m_SpinLock.Release(oldIrql);
740         return STATUS_DELETE_PENDING;
741     }
742 
743     //
744     // ChildObject can't already have a parent, and can't
745     // be its own parent.
746     //
747     ASSERT(ChildObject->m_ParentObject == NULL);
748     ASSERT(IsListEmpty(&ChildObject->m_ChildEntry));
749     ASSERT(ChildObject != this);
750 
751     //
752     // Add to our m_ChildList
753     //
754     InsertTailList(&m_ChildListHead, &ChildObject->m_ChildEntry);
755 
756     if (ChildObject->GetDeviceBase() == NULL) {
757         //
758         // Propagate the device base downward to the child
759         //
760         ChildObject->SetDeviceBase(GetDeviceBase());
761     }
762 
763     m_SpinLock.Release(oldIrql);
764 
765     return STATUS_SUCCESS;
766 }
767 
768 _Must_inspect_result_
769 NTSTATUS
770 FxObject::RemoveChildObjectInternal(
771     __in FxObject* ChildObject
772     )
773 /*++
774 
775 Routine Description:
776 
777     Remove a ChildObject from our child associations list.
778 
779     The ChildObject must exist in our list if we are not
780     otherwise disposing or deleting ourselves.
781 
782     If we are not disposing, the child is removed from the list
783     and its reference count is unmodified.
784 
785     If we are disposing, a failure is returned so that the caller
786     does not delete or dereference the child object itself, since
787     this is similar to a cancel IRP race condition.
788 
789 Arguments:
790     ChildObject - the object to remove this object's list of children
791 
792 Returns:
793 
794     STATUS_SUCCESS - Child was removed from the list, no parent Dispose()
795                      can occur.
796 
797     !STATUS_SUCCESS - Child can not be removed from the list, and is being
798                       Disposed by the parent. The caller must *not* delete
799                       the object itself.
800 
801 --*/
802 
803 {
804     KIRQL oldIrql;
805 
806     m_SpinLock.Acquire(&oldIrql);
807 
808     //
809     // Object is already being deleted, this object will be removed as a child
810     // by the parents Dispose()
811     //
812     if (m_ObjectState != FxObjectStateCreated) {
813         TraceDroppedEvent(FxObjectDroppedEventRemoveChildObjectInternal);
814         m_SpinLock.Release(oldIrql);
815         return STATUS_DELETE_PENDING;
816     }
817 
818     //
819     // We should be the child object's parent
820     //
821     ASSERT(ChildObject->m_ParentObject == this);
822 
823     //
824     // Child should be on our list
825     //
826     ASSERT(!IsListEmpty(&ChildObject->m_ChildEntry));
827 
828     //
829     // We should have entries if someone wants to remove from our list
830     //
831     ASSERT(!IsListEmpty(&m_ChildListHead));
832 
833     RemoveEntryList(&ChildObject->m_ChildEntry);
834 
835     //
836     // Mark it removed
837     //
838     InitializeListHead(&ChildObject->m_ChildEntry);
839 
840     //
841     // We did not take a reference on the child object when it
842     // was added to the list, so we do not dereference it
843     // on the remove call.
844     //
845     // Note: We only dereference child objects when we are deleted
846     //       ourselves, not when the child object manually breaks the
847     //       association by calling this method.
848     //
849     m_SpinLock.Release(oldIrql);
850 
851     return STATUS_SUCCESS;
852 }
853 
854 _Must_inspect_result_
855 FxObject*
856 FxObject::GetParentObjectReferenced(
857     __in PVOID Tag
858     )
859 
860 /*++
861 
862 Routine Description:
863      Return this objects parent, which could be NULL if
864      the object is not part of an association, or this or
865      the parent object is deleting.
866 
867      An extra reference is taken on the parent object which
868      must eventually be released by the caller.
869 
870 Arguments:
871     Tag - Tag to use when referencing the parent
872 
873 Returns:
874 
875     Parent object, otherwise NULL if no parent for this object
876 
877 --*/
878 
879 {
880     KIRQL oldIrql;
881     FxObject* parentObject;
882 
883     m_SpinLock.Acquire(&oldIrql);
884 
885     if (m_ObjectState == FxObjectStateCreated) {
886         parentObject = m_ParentObject;
887     }
888     else {
889         // Parent is disposing us, or we are being disposed
890         parentObject = NULL;
891     }
892 
893     if (parentObject != NULL) {
894         parentObject->ADDREF(Tag);
895     }
896 
897     m_SpinLock.Release(oldIrql);
898 
899     return parentObject;
900 }
901 
902 _Must_inspect_result_
903 NTSTATUS
904 FxObject::Commit(
905     __in_opt    PWDF_OBJECT_ATTRIBUTES Attributes,
906     __out_opt   WDFOBJECT*             ObjectHandle,
907     __in_opt    FxObject* Parent,
908     __in        BOOLEAN  AssignDriverAsDefaultParent
909     )
910 /*++
911 
912 Routine Description:
913      Commit the object before returning the handle to the caller.
914 
915 Arguments:
916      Attributes - PWDF_OBJECT_ATTRIBUTES to assign to this object
917 
918      ObjectHandle - Location to return the objects handle
919 
920 Returns:
921      NTSTATUS of the result. STATUS_SUCCESS if success.
922 
923      Returns WDFOBJECT handle if success.
924 
925 --*/
926 {
927     NTSTATUS status;
928     WDFOBJECT object;
929     FxObject* parent;
930 
931     parent = NULL;
932 
933     if (m_ObjectSize == 0) {
934         ASSERTMSG("Only external objects can call Commit()\n",
935                   m_ObjectSize != 0);
936         return STATUS_INVALID_HANDLE;
937     }
938 
939     //
940     // For an object to be committed into a handle, it needs to have an object
941     // size.  Internal objects set their size to zero as the indication they
942     // are internal and will not be converted into handles.
943     //
944     ASSERT(m_ObjectSize != 0);
945 
946     //
947     // Caller has already validated basic WDF_OBJECT_ATTRIBUTES
948     // with FxValidateObjectAttributes
949     //
950 
951     //
952     // Set object execution level constraint if specified
953     //
954     if (Attributes != NULL &&
955         Attributes->ExecutionLevel == WdfExecutionLevelPassive) {
956         MarkPassiveCallbacks();
957     }
958 
959     //
960     // Assign parent if supplied
961     //
962     if (Parent != NULL) {
963         parent = Parent;
964     }
965     else if (Attributes != NULL && Attributes->ParentObject != NULL) {
966         FxObjectHandleGetPtr(
967             m_Globals,
968             Attributes->ParentObject,
969             FX_TYPE_OBJECT,
970             (PVOID*)&parent
971             );
972     }
973     else {
974 
975         //
976         // If the object already does not have a parent, and
977         // one has not been specified we default it to FxDriver.
978         //
979         // We check to ensure we are not FxDriver being created.
980         //
981         if (AssignDriverAsDefaultParent &&
982             m_ParentObject == NULL) {
983 
984                 //parent = FxToObjectItf::FxGetDriverAsDefaultParent(m_Globals, this);
985 
986 
987                 if (m_Globals->Driver != this) {
988                     parent = m_Globals->Driver;
989                 }
990 
991         }
992     }
993 
994     ASSERT(parent != this);
995 
996     if (parent != NULL) {
997         //
998         // Make it the parent of this object
999         //
1000         status = AssignParentObject(parent);
1001 
1002         if (!NT_SUCCESS(status)) {
1003             return status;
1004         }
1005     }
1006 
1007     //
1008     // Now assign the optional EvtObjectCleanup, EvtObjectDestroy callbacks
1009     //
1010     if (Attributes != NULL) {
1011         FxContextHeader* pHeader;
1012 
1013         pHeader = GetContextHeader();
1014 
1015         if (Attributes->EvtDestroyCallback != NULL) {
1016             pHeader->EvtDestroyCallback = Attributes->EvtDestroyCallback;
1017         }
1018 
1019         if (Attributes->EvtCleanupCallback != NULL) {
1020             pHeader->EvtCleanupCallback = Attributes->EvtCleanupCallback;
1021             m_ObjectFlags |= FXOBJECT_FLAGS_HAS_CLEANUP;
1022         }
1023     }
1024 
1025     //
1026     // We mark the handle as committed so that we can create the handle.
1027     //
1028     MarkCommitted();
1029 
1030     //
1031     // Create the object handle, assign EvtObjectCleanup, EvtObjectDestroy
1032     //
1033     FxObjectHandleCreate(this, &object);
1034 
1035     if (ObjectHandle != NULL) {
1036         *ObjectHandle = object;
1037     }
1038 
1039     return STATUS_SUCCESS;
1040 }
1041 
1042 _Must_inspect_result_
1043 NTSTATUS
1044 FxObject::_GetEffectiveLock(
1045     __in        FxObject* Object,
1046     __in_opt    IFxHasCallbacks* Callbacks,
1047     __in        BOOLEAN AutomaticLocking,
1048     __in        BOOLEAN PassiveCallbacks,
1049     __out       FxCallbackLock** CallbackLock,
1050     __out_opt   FxObject** CallbackLockObject
1051     )
1052 /*++
1053 
1054 Routine Description:
1055 
1056     This gets the effective lock based on the callback constraints
1057     configuration of the supplied object.
1058 
1059     This is a common routine shared by all FxObject's that utilize
1060     DPC's. Currently, this is FxDpc, FxTimer, and FxInterrupt.
1061 
1062     This contains the common serialization configuration logic for these
1063     objects.
1064 
1065 Arguments:
1066 
1067     Object - Object to serialize with
1068 
1069     Callbacks - Optional interface for acquiring constraints and locking pointers
1070 
1071     AutomaticLocking - TRUE if automatic serialization with Object is required
1072 
1073     PassiveCallbacks - TRUE if the caller requires passive level callback, FALSE
1074                        if the
1075     CallbackLock - Lock that is in effect for Object
1076 
1077     CallbackLockOjbect - FxObject that contains the callback lock
1078 
1079 Returns:
1080 
1081     NTSTATUS
1082 
1083 --*/
1084 {
1085     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1086     WDF_EXECUTION_LEVEL parentLevel;
1087     WDF_SYNCHRONIZATION_SCOPE parentScope;
1088 
1089     pFxDriverGlobals = Object->GetDriverGlobals();
1090     *CallbackLock = NULL;
1091     *CallbackLockObject = NULL;
1092 
1093     //
1094     // No automatic locking, nothing to do
1095     //
1096     if (AutomaticLocking == FALSE) {
1097         return STATUS_SUCCESS;
1098     }
1099 
1100     //
1101     // Objects that have callback locks must support this interface.
1102     //
1103     if (Callbacks == NULL) {
1104         return STATUS_INVALID_DEVICE_REQUEST;
1105     }
1106 
1107     //
1108     // Get the callback constraints in effect for the object
1109     //
1110     Callbacks->GetConstraints(&parentLevel, &parentScope);
1111 
1112     if (parentScope == WdfSynchronizationScopeInheritFromParent ||
1113         parentScope == WdfSynchronizationScopeNone) {
1114         //
1115         // Do nothing, no synchronization specified
1116         //
1117         DO_NOTHING();
1118     }
1119     else {
1120         //
1121         // If the caller wants passive callbacks and the object does not support
1122         // it, failure.
1123         //
1124         // If the caller wants non passive callbacks and the object supports
1125         // passive only callbacks, failure.
1126         //
1127         if ((PassiveCallbacks && Object->IsPassiveCallbacks() == FALSE) ||
1128             (PassiveCallbacks == FALSE && Object->IsPassiveCallbacks())) {
1129             FxVerifierDbgBreakPoint(pFxDriverGlobals);
1130             return STATUS_WDF_INCOMPATIBLE_EXECUTION_LEVEL;
1131         }
1132 
1133         *CallbackLock = Callbacks->GetCallbackLockPtr(CallbackLockObject);
1134     }
1135 
1136     return STATUS_SUCCESS;
1137 }
1138