When exactly can you release FCB memory, again!

I have read http://www.osronline.com/article.cfm?article=102 and I can see some of the complications regarding FileObjects. I have not yet seen any “Flags & FO_STREAM_FILE” (I ASSERT on it in my IRP dispatcher) so I have not yet done code to “remember” FileObjects (that did not come via MJ_CREATE).

I reference count in CREATE, and CLEANUP.

Then try to work out when I can free my FCB in CLOSE.

` if (IrpSp->FileObject->FsContext) {
vnode_t *vp = IrpSp->FileObject->FsContext;

                            if (vp->v_iocount == 1 && vp->v_usecount == 0) {

                                    section = vnode_sectionpointer(vp);

                                    if (section->ImageSectionObject) 
                                            (VOID)MmFlushImageSection(section, MmFlushForWrite);

                                    if (FlagOn(IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED) &&
                                            section && section->DataSectionObject) {

                                            CcFlushCache(section, NULL, 0, &iosb);

                                            CcPurgeCacheSection(section, NULL, 0, FALSE);
                                    }

                                    if (IrpSp->FileObject->SectionObjectPointer) {

                                                    KeInitializeEvent(&UninitializeCompleteEvent.Event,
                                                            SynchronizationEvent,
                                                            FALSE);

                                                    CcUninitializeCacheMap(IrpSp->FileObject, NULL, &UninitializeCompleteEvent);
                                                    WaitStatus = KeWaitForSingleObject(&UninitializeCompleteEvent.Event,
                                                            Executive,
                                                            KernelMode,
                                                            FALSE,
                                                            NULL);

                                                    ASSERT(IrpSp->FileObject->SectionObjectPointer->ImageSectionObject == NULL);
                                                    ASSERT(IrpSp->FileObject->SectionObjectPointer->DataSectionObject == NULL);
                                                    IrpSp->FileObject->SectionObjectPointer = NULL;

                                    }
                                    FsRtlTeardownPerStreamContexts(&vp->FileHeader);
                                    FsRtlUninitializeFileLock(&vp->lock);
                            }

                            if (vp->v_iocount == 1 && vp->v_usecount == 0 &&
                                    (IrpSp->FileObject->SectionObjectPointer == NULL ||
                                    (IrpSp->FileObject->SectionObjectPointer->ImageSectionObject == NULL &&
                                            IrpSp->FileObject->SectionObjectPointer->DataSectionObject == NULL))) {

                                                            vnode_recycle(vp); // Release the memory at FsContext
                                    }

                            } else {
                                    dprintf("IRP_CLOSE but can't close yet.\n");
                            }
                            IrpSp->FileObject->FsContext = NULL;

`

So, it sucks to read other’s code - so I’ll try to summarise. Before I release the memory, I made sure that:

  • ImageSectionObject is NULL
  • DataSectionObject is NULL
  • CcUninitializeCacheMap() is called and we wait on it (and we can handle that it may call CLOSE on us!)
  • Free memory
  • FsContext set to NULL

Most of the time it works, but every now and then we die:

nt!KiGeneralProtectionFault+0x2dc C/C++/ASM FLTMGR!FltpGetStreamListCtrl+0x66 C/C++/ASM FLTMGR!FltpPerformPreCallbacks+0x68f C/C++/ASM FLTMGR!FltpPassThroughInternal+0x8c C/C++/ASM FLTMGR!FltpPassThrough+0x144 C/C++/ASM FLTMGR!FltpDispatch+0x9e C/C++/ASM nt!IofCallDriver+0x59 C/C++/ASM nt!IopDeleteFile+0x124 C/C++/ASM nt!ObpRemoveObjectRoutine+0x80 C/C++/ASM nt!ObfDereferenceObject+0xa1 C/C++/ASM nt!MiSegmentDelete+0x192 C/C++/ASM nt!MiProcessDereferenceList+0xa3 C/C++/ASM nt!MiDereferenceSegmentThread+0x125 C/C++/ASM nt!PspSystemThreadStartup+0x47 C/C++/ASM nt!KiStartSystemThread+0x16 C/C++/ASM

I believe that IofCallDriver (or there abouts) calls my MJ_CLOSE, and I detect that I can finally release the memory - the memory allocator scribbles 0xdeadbeef to the memory - then the kernel keeps going, and ends up in FltpGetStreamListCtrl() and dies due to access to 0xdeadbeef.

So why is it still rogering around in “my memory”? Which part is it trying to access? As it says StreamList, I would guess the AdvancedHeader is in play, but I call FsRtlTeardownPerStreamContexts()!

Is there something I have missed in my MJ_CLOSE “can we free memory conditional” ?

If I never release memory, we do not crash - but I don’t see that as a great long term plan.

This is a file system right? If this not then you are waaay off track.

You can delete an FCB (or any other structure) when the last thread stops referencing it and any other structures you have stop _referencing _it. It is you, not any one else, who decides which data structures point to which other ones. If you decide to point a FileObject->FsContext at an FCB then you can be sure that when you get the MJ_CLOSE you are never going to see that FileObject again and so that route to the FCB is gone.

The IoCreateFileObject functions are a red herring, nobody but you is going to point FileObject->FsContext at your FCB

So, before you get much older you are going to have to write reference counting logic and free the fcb when it the ref count goes to zero. That is way simpler that all the other stuff you are trying to do - which serves absolutely no purpose and mostly likely will confuse things (for instance CcPurgeCacheSection should provoke MJ_CLOSE, not vice versa.

The stack you posted looks like you some other file object has a reference to FsContext, but you have blown away the fcb

_ If you decide to point a FileObject->FsContext at an FCB then you can be sure that when you get the MJ_CLOSE you are never going to see that FileObject again_

Yeah but that’s just it, I decide to share my memory with a FileObject->FsContext, only to find the kernel has created new FileObjects, and re-shared my memory with those, like some key-party I didn’t sign up for!

But, now I record all FileObjects I see with my memory, and I will admit many crashes has gone away - I still need to do the CcUninitializeCacheMap() - when I delete a .exe file, the last CLOSE would take up to 10 mins otherwise.

I guess I shouldn’t have hesitated, but it just seem so strange to not have an IRP call for “assigning to another FO” so the FSDs can have an easier time.