/*++

    Copyright (c) Microsoft Corporation.  All rights reserved.

Rule Name:

    RemoveLockForwardWrite2

Domain:

    wdm

Rule ID:

    Not Applicable

Description:

    This rule verifies that calls to IoAcquireRemoveLock and IoReleaseRemoveLock are used correctly when forwarding an IRP using IoCallDriver to a another device.

Help Link:

    https://go.microsoft.com/fwlink/?LinkId=507311

--*/


#include "slic_base.h"

state
{
   int LockDepth = 0;
   enum {initial,finished} completion = initial;
   enum {init,on} t = init;
}watch fun_IRP_MJ_WRITE.entry.$2;

[fun_IRP_MJ_WRITE].entry[guard $2]
{
    if($2->Tail.Overlay.CurrentStackLocation->MajorFunction==IRP_MJ_WRITE)
    {
        t=on;
    }
    else
    {
        halt;
    }
}



[IoCsqInsertIrp,
IoCsqInsertIrpEx].entry[guard $2]
{
    halt;
}


[ExInterlockedInsertHeadList
,ExInterlockedInsertTailList
,ExInterlockedPushEntryList
,ExfInterlockedInsertHeadList
,ExfInterlockedInsertTailList
,ExfInterlockedPushEntryList].entry 
{
    halt;
}

[InsertHeadList
,InsertTailList].entry
{
    halt;
}

[RemoveHeadList,sdv_containing_record].entry
{
    halt;
}

IoAcquireRemoveLock.exit
{
    if(LockDepth > 0 && $return!=STATUS_SUCCESS)
    {
       halt;
    }
    else if($return==STATUS_SUCCESS)
    {
       LockDepth = LockDepth + 1;
    }
}

[IoReleaseRemoveLock].entry
{
   if(LockDepth == 0&&t==on)
   {
       abort "Removelock should be acquired before it is released.";
   }
   else 
   {
      LockDepth = LockDepth - 1;
   }
}

[IoCallDriver,PoCallDriver].entry
{  
   if (LockDepth == 0&&t==on) 
   {
       abort "The routine was called without holding the Removelock.";
   }
}


[SDV_IO_COMPLETION_ROUTINE].exit
{
    if(LockDepth != 0&&t==on)
    {
        completion = finished;
        
    }
    
}


[fun_IRP_MJ_WRITE].exit[guard $2]
{
    if(LockDepth != 0&&$2->Tail.Overlay.CurrentStackLocation->MajorFunction==IRP_MJ_WRITE&&completion ==finished&&LockDepth != 0)
    {
        abort "Removelock should be released within the Dispatch routine.";
    }
    else 
    {
        halt;
    }
}
