/*++

    Copyright (c) Microsoft Corporation.  All rights reserved.

Rule Name:

    RemoveLockMnRemove2

Domain:

    wdm

Rule ID:

    Not Applicable

Description:

    This rule verifies that calls to IoAcquireRemoveLock and IoReleaseRemoveLockAndWait are used correctly when processing
    IRP_MJ_PNP with MinorFunction IRP_MN_REMOVE_DEVICE before the IRP is forwarded to lower drivers.
    This rule only applies to FDO and FIDO drivers.
	
    here is the pnp stack
 
    filter
     |
    fdo
     |
    pdo
 
    pnp manager has sent a query remove through the stack 
    fdo is enabled to idle while the system is running.  it decides to power down in the query removed state so it requests a D0 irp 
    before the d0 irp arrives, the pnp manager sends a pnp remove irp and that is processed by the filter.  the filter detaches from the stack and cleans up its state 
    d0 arrives at the top of the stack, filter does not send it down the stack b/c it has no context/data to know where to send it anymore 
    fdo is hung waiting for the d0 irp to arrive and it never will


    1.	Before a device is detached from the device stack IoAcquireRemoveLock must succeed before the IRP is forwarded down the stack.For the following Irp Types IRP_MN_QUERY_REMOVE,IRP_MN_SUPRISE_REMOVAL and IRP_MN_REMOVE_DEVICE.
    2.	IoReleaseRemoveLockAndWait should be called before calling IoDetachDevice or IoDeleteDevice. (This makes sure that all removelocks are released in device drivers).

Help Link:

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

--*/


#include "slic_base.h"

state
{
  int LockDepth = 0;
  enum {init,on} t = init;
  enum {initial,locked,release_and_wait} remove_lock_state = initial;
}
watch sdv_RunRemoveDevice.entry.$2;


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

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

[IoReleaseRemoveLock].entry
{
    if (LockDepth == 0&&t==on) 
    {
       abort "The driver is calling $fname without first acquiring the Removelock.";
    } 
    else 
    {
       LockDepth = LockDepth - 1;
    }
}

[IoReleaseRemoveLockAndWait].entry
{
    if (LockDepth == 0&&t==on) 
    {
       abort "The driver is calling $fname without first acquiring the Removelock.";
    } 
    else 
    {
       LockDepth = LockDepth - 1;
       remove_lock_state = release_and_wait;
    }
}


[IoCallDriver,PoCallDriver].entry[guard $2]
{  
   
   if (remove_lock_state == release_and_wait&&t==on) 
   {
         abort "The routine was called after IoReleaseRemoveLockAndWait was called.";
   }
   else if (remove_lock_state == initial&&t==on) 
   {
         abort "The routine was called without acquiring a removelock.";
   }
   else
   {
       halt;
   }

}

