<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>A Hole In My Head : WDM</title><link>http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx</link><description>Tags: WDM</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>How do I cancel an IRP that another thread may be completing at the same time?</title><link>http://blogs.msdn.com/doronh/archive/2008/06/29/how-do-i-cancel-an-irp-that-another-thread-may-be-completing-at-the-same-time.aspx</link><pubDate>Mon, 30 Jun 2008 09:16:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8670424</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/8670424.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=8670424</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=8670424</wfw:comment><description>&lt;P&gt;Let's say that you allocated a PIRP and sent it down your device stack.&amp;nbsp; You free the PIRP in the completion routine and then return STATUS_MORE_PROCESSING_REQUIRED.&amp;nbsp; To make life more fun, you decide that you want to be able to cancel the sent IRP after you have sent it so you try to do it simple like this&lt;/P&gt;&lt;PRE&gt;typedef struct _DEVICE_EXTENSION {
    KSPIN_LOCK SentIrpLock;
    PIRP SentIrp;
} DEVICE_EXTENSION;&lt;/PRE&gt;
&lt;P&gt;Sending thread:&lt;/P&gt;&lt;PRE&gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
devext-&amp;gt;SentIrp = Irp;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&lt;/PRE&gt;
&lt;P&gt;Canceling thread:&lt;/P&gt;&lt;PRE&gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
if (devext-&amp;gt;AllocatedIrp != NULL) {
   IoCancelIrp(devext-&amp;gt;SentIrp);
}
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&lt;/PRE&gt;
&lt;P&gt;Completion routine:&lt;/P&gt;&lt;PRE&gt;PIRP irp;

KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
irp = devext-&amp;gt;SentIrp;&lt;/PRE&gt;&lt;PRE&gt;devext-&amp;gt;SentIrp = NULL;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);

IoFreeIrp(irp);

return STATUS_MORE_PROCESSING_REQUIRED;&lt;/PRE&gt;
&lt;P&gt;And it then deadlocks ;). If the call to IoCancelIrp causes the IRP to be completed in the calling context (e.g. the one which has acquired the lock), the completion routine will run and try to acquire the lock (SentIrpLock) on the same thread which holds it.&lt;/P&gt;
&lt;P&gt;So, life is not that simple and you have to do something more. The basic solution is that you need extra state to track who is touching the PIRP and who can free it. Walter Oney's book has a solution (IIRC, it is in the self initiated I/O section, but I do not have the book handy), but IMHO it is a bit complicated. KMDF has a solution to this problem which I like much more (imagine that ;)).&lt;/P&gt;
&lt;P&gt;You need an extra LONG, calling it CompletionCount per PIRP that you want to be able to cancel.&lt;/P&gt;&lt;PRE&gt;&amp;nbsp;&lt;/PRE&gt;
&lt;OL&gt;
&lt;LI&gt;You initialize CompletionCount to 1 before sending it down the stack and storing it in devext. &lt;/LI&gt;
&lt;LI&gt;Whenever there is a thread that wants to cancel the PIRP, it tries to interlocked increment CompletionCount only if and only if the current CompletionCount value is &amp;gt; 0. For this you need to roll your own InterlockedIncrementWithFloor which is fortunately not that hard and I have already shown you how to do that, &lt;A href="http://blogs.msdn.com/doronh/archive/2006/12/06/creating-your-own-interlockedxxx-operation.aspx" mce_href="http://blogs.msdn.com/doronh/archive/2006/12/06/creating-your-own-interlockedxxx-operation.aspx"&gt;http://blogs.msdn.com/doronh/archive/2006/12/06/creating-your-own-interlockedxxx-operation.aspx&lt;/A&gt;. &lt;/LI&gt;
&lt;LI&gt;After the canceling thread has called InterlockedIncrementWithFloor and IoCancelIrp, it calls InterlockedDecrement. &lt;/LI&gt;
&lt;LI&gt;Whomever wants to complete the PIRP, like the completion routine, interlock decrements CompletionCount. &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;If the returned value from InterlockedDecrement is zero, the caller can complete the PIRP. If not, somebody else is trying to touch the PIRP and you must leave the PIRP alone.&amp;nbsp; So here is the revised code:&lt;/P&gt;&lt;PRE&gt;typedef struct _DEVICE_EXTENSION {
    KSPIN_LOCK SentIrpLock;
    PIRP SentIrp;
    ULONG CompletionCount;
} DEVICE_EXTENSION;&lt;/PRE&gt;
&lt;P&gt;Sending thread:&lt;/P&gt;&lt;PRE&gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
&lt;FONT color=#ff0000&gt;devext-&amp;gt;CompletionCount = 1;
&lt;/FONT&gt;devext-&amp;gt;SentIrp = Irp;&lt;/PRE&gt;&lt;PRE&gt;KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&lt;/PRE&gt;
&lt;P&gt;Canceling thread:&lt;/P&gt;&lt;PRE&gt;PIRP irp = NULL;
KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
if (devext-&amp;gt;AllocatedIrp != NULL &lt;FONT color=#ff0000&gt;&amp;amp;&amp;amp; MyInterlockedIcrementeWithFloor(&amp;amp;devext-&amp;gt;CompletionCount, 0) &amp;gt; 0&lt;/FONT&gt;) {
&lt;FONT color=#ff0000&gt;   irp = devext-&amp;gt;SentIrp;
&lt;/FONT&gt;}
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);

&lt;FONT color=#ff0000&gt;if (irp != NULL) {
    IoCancelIrp(irp);
    if (InterlockedDecrement(&amp;amp;devext-&amp;gt;CompletionCount) == 0) {
        IoFreeIrp(irp);
    }
}
&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;Completion routine:&lt;/P&gt;&lt;PRE&gt;PIRP irp = NULL;

KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
irp = devext-&amp;gt;SentIrp;&lt;PRE&gt;devext-&amp;gt;SentIrp = NULL;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);

&lt;FONT color=#ff0000&gt;if (InterlockedDecrement(&amp;amp;devext-&amp;gt;CompletionCount) == 0) {&lt;/FONT&gt;
    IoFreeIrp(irp);
&lt;FONT color=#ff0000&gt;}&lt;/FONT&gt;

return STATUS_MORE_PROCESSING_REQUIRED;&lt;/PRE&gt;&lt;/PRE&gt;
&lt;P&gt;The beauty of this solution is that if you add more actors (let's say a timer for an async timeout, all you have to do is bump the CompletionCount to account for them to asynchronously rundown if you cannot cancel them.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8670424" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/KMDF/default.aspx">KMDF</category><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Inconceivableable</title><link>http://blogs.msdn.com/doronh/archive/2008/03/18/inconceivableable.aspx</link><pubDate>Wed, 19 Mar 2008 02:55:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8324240</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/8324240.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=8324240</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=8324240</wfw:comment><description>&lt;P&gt;I have no idea who created the name for PNP_DEVICE_NOT_DISABLEABLE, but I probably have the same reaction as you ... "seriously?&amp;nbsp; that is what they named?"&amp;nbsp; I mean come on, I think it could have at least been named PNP_DEVICE_CANNOT_BE_DISABLED.&amp;nbsp; I am sure you can think of some better names too. If so, please leave a comment with your suggestions!&amp;nbsp; While we had a chance to rectify this in KMDF in the &lt;FONT color=#000000&gt;&lt;A class="" href="http://msdn2.microsoft.com/en-us/library/aa491094.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa491094.aspx"&gt;WDF_DEVICE_STATE&lt;/A&gt;&lt;/FONT&gt;&amp;nbsp;structure, we chose to keep the field name (NotDisableable)&amp;nbsp;similar to the WDM name to avoid confusion.&lt;/P&gt;
&lt;P&gt;For any readers who have not encountered this bit, it is a part of &lt;A href="http://msdn2.microsoft.com/en-us/library/ms805981.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms805981.aspx"&gt;PNP_DEVICE_STATE&lt;/A&gt;.&amp;nbsp; You set this bit in the IRP_MJ_PNP/&lt;A href="http://msdn2.microsoft.com/en-us/library/ms806467.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms806467.aspx"&gt;IRP_MN_QUERY_PNP_DEVICE_STATE&lt;/A&gt; IRP after calling &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490455.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa490455.aspx"&gt;IoInvalidateDeviceState&lt;/A&gt;.&amp;nbsp; &lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8324240" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Once not disableable, forever not disableable</title><link>http://blogs.msdn.com/doronh/archive/2008/03/18/once-not-disableable-forever-not-disableable.aspx</link><pubDate>Tue, 18 Mar 2008 18:21:12 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8324256</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/8324256.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=8324256</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=8324256</wfw:comment><description>&lt;p&gt;One interesting quirk about the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms805981.aspx" target="_blank"&gt;PNP_DEVICE_NOT_DISABLEABLE&lt;/a&gt; state is that once it has been set and the PnP manager has processed it, the state is sticky.&amp;#160; By sticky I mean that even if you attempt to clear this bit on a subsequent IRP_MN_QUERY_PNP_DEVICE_STATE IRP, the PnP manager ignores your changes to this state.&amp;#160; This state remains stuck until any of the following occur&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;The machine is rebooted and the device is reenumerated&lt;/li&gt;    &lt;li&gt;The device (or any device in its ancestry) is surprise removed&lt;/li&gt;    &lt;li&gt;The device (or any device in its ancestry) is ejected&lt;/li&gt; &lt;/ol&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8324256" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Why I should not be writing applications ;)</title><link>http://blogs.msdn.com/doronh/archive/2007/11/09/why-i-should-not-be-writing-applications.aspx</link><pubDate>Fri, 09 Nov 2007 22:12:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6025594</guid><dc:creator>doronh</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/doronh/comments/6025594.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=6025594</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=6025594</wfw:comment><description>The first driver I owned when I started at Microsoft in 1997 was i8042prt.sys, the driver that controls your PS2 mouse and keyboard.&amp;nbsp; I had the job of upgrading it from an NT4 legacy style driver to a PnP enabled Windows 2000 driver.&amp;nbsp; Of course my dad asked "didn't keyboards and mice work before you got there?&amp;nbsp; what are you doing over there anyways?"&amp;nbsp; My manager at the time said all I had to do was get the mouse and keyboard working after resume from Sx and that would be it.&amp;nbsp; I ended up owning the driver for 5+ years and not the advertised couple of months ;), but I learned a lot. 
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;One of things I did while owning this driver was unify crash dump support for all builds.&amp;nbsp; Previous to my owning the driver, Microsoft Far East Asia PSS added the ability to crash (aka crash dump) the machine for any given key and modifier combination (see &lt;FONT face="Courier New" size=2&gt;I8xServiceCrashDump&lt;/FONT&gt;()&amp;nbsp;for initialization of state&amp;nbsp;and &lt;FONT face="Courier New"&gt;I8xProcessCrashDump&lt;/FONT&gt;() on processing the keys at runtime in 6000\src\input\pnpi8042 in the WDK for the gory details).&amp;nbsp; This code was under &lt;FONT face="Courier New"&gt;#ifdef&lt;/FONT&gt;s and was only live for far east builds of the driver.&amp;nbsp; This was a universally useful feature and I was asked to make it available for all builds.&amp;nbsp;&amp;nbsp;I&amp;nbsp;decided to make it easy for folks to enable it as well.&amp;nbsp; Configuration was not easy. First, you had to know the scan code for the trigger key (instead of the character itself).&amp;nbsp; Second you had to describe the modifiers using a set of flags that were only documented in the driver's header.&amp;nbsp; Third, you had to know where to put these registry values (which typically meant you also had to create a reg key as well). Yuck, not friendly.&lt;/P&gt;
&lt;P&gt;To fix this I created one registry value (CrashOnCtrlScroll) that setup the default crash dump key sequence for you, right ctrl + scroll lock.&amp;nbsp; This KB &lt;A href="http://support.microsoft.com/kb/244139" target=_blank&gt;article&lt;/A&gt;&amp;nbsp;describes&amp;nbsp;how to set the key.&amp;nbsp;That worked well ... &lt;EM&gt;&lt;STRONG&gt;except &lt;/STRONG&gt;&lt;/EM&gt;that many laptops did not have a right control key!&amp;nbsp; I didn't want to create another key for crashing on left ctrl + scroll lock (or something as arbitrary but completely different) so I created a&amp;nbsp;GUI&amp;nbsp;application that let you type the key and select the modifiers.&amp;nbsp; Not rocket science, but functional.&amp;nbsp; Here is a snapshot&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/doronh/images/6002437/original.aspx" align=middle mce_src="http://blogs.msdn.com/photos/doronh/images/6002437/original.aspx"&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Issue #1&lt;/STRONG&gt;:&amp;nbsp; While functional, it's ugly&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Issue #2&lt;/STRONG&gt;:&amp;nbsp; I named it cdsetup.exe (c[rash]d[ump]setup.exe).&amp;nbsp;&amp;nbsp;Logical name except that many&amp;nbsp;cdrom setup applications are also called cdsetup.exe so there was a bit of confusion.&amp;nbsp; To add&amp;nbsp;to the confusion, I think my version of cdsetup.exe was a part of the Windows 200 resource kit.&amp;nbsp; The one saving grace to this name is that it automatically requests elevation on Vista (probably because it has "setup" its name).&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Issue #3&lt;/STRONG&gt;:&amp;nbsp; After recompiling the application yesterday to test it on Vista the red X, Cancel and OK buttons all stopped working which meant I could not dismiss the dialog box.&amp;nbsp; &lt;EM&gt;Weird&lt;/EM&gt;.&amp;nbsp; I found an old build and it worked just fine so something I did during the recompile altered the behavior.&amp;nbsp; To get the application to build and run I had to add the following 2 lines to the sources file&lt;/P&gt;&lt;PRE&gt;USE_MSVCRT=1
_NT_TARGET_VERSION=$(_NT_TARGET_VERSION_WIN2K)
&lt;/PRE&gt;
&lt;P&gt;Specifying the taret version has no effect on the runtime so it must have been the conversion to the MS VC runtime instead of the old C runtime that shipped with Windows. I decided to debug the application's DlgProc.&amp;nbsp; It is rather simple:&lt;/P&gt;&lt;PRE&gt;LRESULT CALLBACK CrashDumpSetup::s_DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    CrashDumpSetup *cdi;

    if (message == WM_INITDIALOG) {
        cdi = new CrashDumpSetup(hDlg);

        if (!cdi)
            return FALSE;

        SetWindowLongPtr(hDlg, DWLP_USER, (ULONG_PTR) cdi);
        return (LRESULT) cdi-&amp;gt;Initialize();
    }

    cdi = (CrashDumpSetup *) GetWindowLongPtr(hDlg, DWLP_USER);
    if (cdi) {
        return cdi-&amp;gt;DialogProc(message, wParam, lParam);
    }

    return FALSE;
}

LRESULT CrashDumpSetup::DialogProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDC_DUMP_DISABLE:
        case IDC_DUMP_DEFAULT:
        case IDC_DUMP_ALT:
            ToggleAltUI(LOWORD(wParam) == IDC_DUMP_ALT);
            break;

        case IDOK:
            if (Apply() == FALSE) {
                return FALSE;
            }
            __fallthrough;

        case IDCANCEL:
            delete this;
            EndDialog(m_hDlg, LOWORD(wParam));
            break;

        }
    }

    return FALSE;
}
&lt;/PRE&gt;
&lt;P&gt;When pressing the red X, Cancel or OK keys my WM_COMMAND processing code ran and called EndDialog() as I expected, but like I said before,&amp;nbsp;the dialog didn't dismiss itself.&amp;nbsp; I looked at the &lt;A href="http://msdn2.microsoft.com/en-us/library/ms645472.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms645472.aspx"&gt;docs&lt;/A&gt; for EndDialog and saw that it returns a BOOL, so I rewrote&amp;nbsp;EndDialog to capture the return value and it was returning FALSE!&amp;nbsp; Ugh.&amp;nbsp; Since I was single stepping through the function, !gle&amp;nbsp;came to the rescue &lt;PRE&gt;0:000&amp;gt; !gle
LastErrorValue: (Win32) 0x578 (1400) - Invalid window handle.
LastStatusValue: (NTSTATUS) 0xc0000034 - Object Name not found.
&lt;/PRE&gt;
&lt;P&gt;An invalid window handle? m_hDlg worked before, maybe some component was overwriting it. So dumped the variable and then the hDlg from s_DlgProc,&lt;/P&gt;&lt;PRE&gt;0:000&amp;gt; dt this m_hDlg
Local var @ 0x1ff798 Type CrashDumpSetup*
0x00a01f88 
   +0x000 m_hDlg : &lt;FONT color=#ff0000&gt;&lt;STRONG&gt;0x00a064d0&lt;/STRONG&gt;&lt;/FONT&gt; HWND__
0:000&amp;gt; .f+
01 001ff7cc 775af512 cdsetup!CrashDumpSetup::s_DlgProc+0x8f [d:\work\cdsetup\cdsetup.cpp @ 576]
0:000&amp;gt; dt hDlg
Local var @ 0x1ff7d4 Type HWND__*
&lt;FONT color=#ff0000&gt;&lt;STRONG&gt;0x004e05f0&lt;/STRONG&gt;&lt;/FONT&gt; 
   +0x000 unused           : 49595140
&lt;/PRE&gt;
&lt;P&gt;Looking at the 2 values, it is obvious that m_hDlg was altered. But how? After staring at the code I wrote 7 years ago, I thought about what I changed (using the MS C runtime) and I immediate saw it. I was deleting the object and then touching freed memory. The old C runtime left the freed memory as it was before itw as freed so the old code had the bug the entire time, it was just that the new runtime caught made the mistake apparent immediately. The fix was simple, capture the value before deleting the object and then calling EndDialog (I guess I could have called EndDialog in the destructor but that made cleanup in the failure to initialize the object potentially problematic). After applying the fix, all of the buttons now worked as expected ;). &lt;PRE&gt;        case IDCANCEL:
&lt;STRONG&gt;            HWND h = m_hDlg;
&lt;/STRONG&gt;            delete this;
            EndDialog(h &lt;STRIKE&gt;m_hDlg&lt;/STRIKE&gt;, LOWORD(wParam));
            break;

&lt;/PRE&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6025594" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>How to share HW resources with another driver not in the same PnP hierarchy</title><link>http://blogs.msdn.com/doronh/archive/2007/10/24/how-to-share-hw-resources-with-another-driver-not-in-the-same-pnp-hierarchy.aspx</link><pubDate>Thu, 25 Oct 2007 01:56:23 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5657859</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/5657859.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=5657859</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=5657859</wfw:comment><description>&lt;p&gt;First, I have to say that I don't agree with this design pattern &lt;em&gt;at all&lt;/em&gt;.&amp;nbsp; I think it leads to too many problems and complications that are not worth the pain.&amp;nbsp; The only reason I am writing this entry is that I have seen so many people get this wrong or not account for some details and I want to make sure that if such a driver is installed, the stability of the system is not compromised.&amp;nbsp; The scenario I am covering is not the case where you are enumerating child devices and want to share resources assigned to the parent amongst the children.&amp;nbsp; Rather, this is sharing HW resources assigned to your device (device A) with another device (device B).&amp;nbsp; Device B can be a legacy NT4 style device or another PnP device stack, it doesn't matter.&lt;/p&gt; &lt;p&gt;There are&amp;nbsp;3 aspects to the issue that you must solve:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Handing off the resources to device B.&amp;nbsp; This means that device B will open a handle against device A and send an internal IOCTL (e.g. IOCTL_INTERNAL_GET_RESOURCES) which device A completes with the appropriate values.&amp;nbsp; If the number of resources is fixed, this is easy.&amp;nbsp; If the number of resources varies, this can get complicated.  &lt;li&gt;Coordinating access to the resources between device A and device B.&amp;nbsp; If both drivers want to touch the resources at the same time, you now have to synchronize between 2 components and that can get very complicated, especially if there are callbacks involved.  &lt;li&gt;Only touching the resources when the device has power.&amp;nbsp; This is easy for device A to do since it is the power policy owner for the device and sees pnp state changing and device power irps.&amp;nbsp; This is not easy for device B to do though.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Aspects number 1 and 2 I will leave for you to solve.&amp;nbsp; Number 3 is where things get interesting.&amp;nbsp; There are scenarios where the device can lose power:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;font color="#80ff00"&gt;PnP graceful remove (query remove, remove) &lt;/font&gt; &lt;li&gt;&lt;font color="#80ff00"&gt;PnP surprise removal&lt;/font&gt;  &lt;li&gt;PnP stop  &lt;li&gt;System power state change (e.g. a standby or hibernate)&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&amp;nbsp;The first 2 scenarios in &lt;font color="#80ff00"&gt;green &lt;/font&gt;are easily solved.&amp;nbsp; Device B registers for PnP notifications on the file handle that it opens and it will be told when a PnP remove or surprise removal occurs.&amp;nbsp; If you are using KMDF, the WDFIOTARGET automatically does this for you.&amp;nbsp; But there is a catch to the surprise removal notification...it is sent to device B after device A has already processed the PnP IRP.&amp;nbsp; This means that there is a window where device B thinks it has valid resources, but in reality it does not.&amp;nbsp; File handle notifications do not solve the last 2 scenarios though and the surprise removal case does not work well, so it would be nice if there was one solution for all 4 scenarios.&lt;/p&gt; &lt;p&gt;The solution is that device B must register 2 callbacks with device A, a power up and power down callback (they can be condensed to one callback with an additional passed in parameter indicating power state if you want).&amp;nbsp; These callbacks should be set at the same time that the resources are acquired so that there is no window where the resources have been acquired, the device loses power and the callback has not yet been set notifying device B of the power state change.&amp;nbsp; These callbacks can be passed from device B to device in any number of ways.&amp;nbsp; Two traditional ways are&lt;/p&gt; &lt;ul&gt; &lt;li&gt;To pass the callbacks in a buffer in an internal IOCTL (you can overload IOCTL_INTERNAL_GET_RESOURCES if you want)  &lt;li&gt;To send a IRP_MN_QUERY_INTERFACE with these callbacks as input values in the INTERFACE based structure as I describe in &lt;a href="http://blogs.msdn.com/doronh/archive/2007/03/06/interfaces-can-contain-both-input-and-output-parameters-and-not-just-function-pointers.aspx" target="_blank"&gt;this post&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Once the callbacks have been set, device A must call them at the right time.&amp;nbsp; In a KMDF driver, it is a very simple implementation.&amp;nbsp; You call device B's power up callback in &lt;a href="http://msdn2.microsoft.com/en-us/library/aa490952.aspx" target="_blank"&gt;EvtDeviceSelfManagedIoRestart&lt;/a&gt;&amp;nbsp;and device B's power down routine in &lt;a href="http://msdn2.microsoft.com/en-us/library/aa490940.aspx" target="_blank"&gt;EvtDeviceSelfManagedIoSuspend&lt;/a&gt;.&amp;nbsp; This covers all cases because KMDF treats PnP state changes as implicit power changes, powering down the device and using the same callbacks for all cases.&amp;nbsp; This also covers the surprise removal window because device B is now notified at the beginning of device A's power down instead of after it.&amp;nbsp; One final thing to consider is what device B thinks the initial power state of device A is in.&amp;nbsp; Ideally, device B should assume device A is in low power and should not touch the retrieved resources until its power up callback has been invoked.&amp;nbsp; This would mean that the initial call to device B's power up call might occur outside of EvtDeviceSelfManagedIoRestart, perhaps immediately after completing the IOCTL_INTERNAL_GET_RESOURCES request.&lt;/p&gt; &lt;p&gt;If you are writing a WDM driver, well, I will leave that implementation up to you ;).&amp;nbsp; &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5657859" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/KMDF/default.aspx">KMDF</category><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category><category domain="http://blogs.msdn.com/doronh/archive/tags/Design+Patterns/default.aspx">Design Patterns</category></item><item><title>Setting a security descriptor on a legacy device object</title><link>http://blogs.msdn.com/doronh/archive/2007/10/16/setting-a-security-descriptor-on-a-legacy-device-object.aspx</link><pubDate>Wed, 17 Oct 2007 02:45:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5477716</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/5477716.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=5477716</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=5477716</wfw:comment><description>&lt;P&gt;Setting the security descriptor allows you to control who can open a handle to the device object.&amp;nbsp; Typically you can call &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490540.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa490540.aspx"&gt;IoCreateDeviceSecure&lt;/A&gt; to create the device object and have the correct DACL from the start.&amp;nbsp; One issue with IoCreateDeviceSecure is that the SDDL string is limited to what it can describe, primarily you can only specify predefined SIDs.&amp;nbsp; If you need a SID that is not predefined you must build the security descriptor by hand and the code to do it is not very easty to implement (let alone figure out which DDIs you need to call in the first place).&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Well, Paul Sliwowicz kindly donated this code snippet which will set a security descriptor on a legacy device object.&amp;nbsp; It does require that you are able to open a handle to the device object first, so it might be a good idea to initially create the device with a very restrictive SDDL string that limits opens to the system only and then relax it to your needs with the following code which works in either a WDM or KMDF driver.&amp;nbsp;&amp;nbsp; This can also work on device objects that are created by other drivers, but you have a built in race between when the device is created and when you set the security descriptor that you must also account for.&lt;/P&gt;&lt;PRE&gt;NTSTATUS
SetDeviceDacl(
    PDEVICE_OBJECT DeviceObject
    )
{
    SECURITY_DESCRIPTOR sd = { 0 };
    ULONG aclSize = 0;
    PACL pAcl = NULL;&lt;/PRE&gt;&lt;PRE&gt;    NTSTATUS status;

    status = &lt;A href="http://msdn2.microsoft.com/en-us/library/ms796469.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms796469.aspx"&gt;ObOpenObjectByPointer&lt;/A&gt;(DeviceObject,
                                   OBJ_KERNEL_HANDLE,
                                   NULL,
                                   WRITE_DAC,
                                   0,
                                   KernelMode,
                                   &amp;amp;fileHandle);

    if (!NT_SUCCESS(status)) {
        goto Exit;
    } 

    //
    // Calculate how big our ACL needs to be to support one ACE (in
    // this case we'll use a predefined Se export for local system
    // as the SID)
    //
    aclSize = sizeof(ACL);
    aclSize += &lt;A href="http://msdn2.microsoft.com/en-us/library/ms796955.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms796955.aspx"&gt;RtlLengthSid&lt;/A&gt;(&lt;A class="" href="http://msdn2.microsoft.com/en-us/library/aa489494.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa489494.aspx"&gt;SeExports&lt;/A&gt;-&amp;gt;SeLocalSystemSid);
    aclSize += RtlLengthSid(SeExports-&amp;gt;SeAliasAdminsSid);
    aclSize += RtlLengthSid(SeExports-&amp;gt;SeAliasUsersSid);
   
    //
    // Room for 3 ACEs (one for each SID above); note we don't
    // include the end of the structure as we've accounted for that
    // length in the SID lengths already.
    //
    aclSize += 3 * FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart);

    pAcl = (PACL) ExAllocatePoolWithTag(PagedPool, aclSize, TAG);

    if (pAcl == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    status = &lt;A href="http://msdn2.microsoft.com/en-us/library/ms796739.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms796739.aspx"&gt;RtlCreateAcl&lt;/A&gt;(pAcl, aclSize, ACL_REVISION);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    status = &lt;A href="http://msdn2.microsoft.com/en-us/library/ms796968.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms796968.aspx"&gt;RtlAddAccessAllowedAce&lt;/A&gt;(pAcl,
                                    ACL_REVISION,
                                    GENERIC_READ | GENERIC_WRITE | DELETE,
                                    SeExports-&amp;gt;SeLocalSystemSid);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    status = RtlAddAccessAllowedAce(pAcl,
                                    ACL_REVISION,
                                    GENERIC_READ | GENERIC_WRITE | DELETE,
                                    SeExports-&amp;gt;SeAliasAdminsSid );
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    status = RtlAddAccessAllowedAce(pAcl,
                                    ACL_REVISION,
                                    GENERIC_READ,
                                    SeExports-&amp;gt;SeAliasUsersSid );&lt;/PRE&gt;&lt;PRE&gt;    if (!NT_SUCCESS(status)) {
        goto Exit;
    }
 
    //
    // Create a security descriptor
    //
    status = &lt;A href="http://msdn2.microsoft.com/en-us/library/ms802990.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms802990.aspx"&gt;RtlCreateSecurityDescriptor&lt;/A&gt;(&amp;amp;sd, SECURITY_DESCRIPTOR_REVISION);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    //
    // Associate the above pAcl with the security descriptor
    //
    status = &lt;A href="http://msdn2.microsoft.com/en-us/library/ms804297.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms804297.aspx"&gt;RtlSetDaclSecurityDescriptor&lt;/A&gt;(&amp;amp;sd, TRUE, pAcl, FALSE);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

    //
    // Set security on the object
    //
    status = &lt;A href="http://msdn2.microsoft.com/en-us/library/ms800901.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms800901.aspx"&gt;ZwSetSecurityObject&lt;/A&gt;(fileHandle, DACL_SECURITY_INFORMATION, &amp;amp;sd);
    if (!NT_SUCCESS(status)) {
        goto Exit;
    }

Exit:
    ZwClose(fileHandle);
    fileHandle = NULL;

    if (pAcl != NULL) {
        ExFreePool(pAcl);
        pAcl = NULL;
    }

    return status;
}
&lt;/PRE&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5477716" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/KMDF/default.aspx">KMDF</category><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Fast Resume and how if affects your driver</title><link>http://blogs.msdn.com/doronh/archive/2007/10/15/fast-resume-and-how-if-affects-your-driver.aspx</link><pubDate>Mon, 15 Oct 2007 20:33:02 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5462922</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/5462922.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=5462922</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=5462922</wfw:comment><description>&lt;p&gt;Fast resume,&amp;nbsp;which was introduced in Windows XP, &amp;nbsp;is often mentioned when implementing power support in your WDM driver.&amp;nbsp; But what does "fast resume" mean and when implementing fast resume, what side effects occur in your driver?&amp;nbsp; I'll to&amp;nbsp;answer both of these questions as well as the&amp;nbsp;reasoning behind this feature.&amp;nbsp; When I started to write this entry, I tried to look up fast resume in the WDK and found absolutely nothing!&amp;nbsp; I was shocked since this is a very common term (at least internally at Microsoft); after speaking with the power team, there is a &lt;a href="http://www.microsoft.com/whdc/system/sysperf/resumeperf.mspx" target="_blank"&gt;white paper&lt;/a&gt; that describes fast resume but you would never know it from the title "Measuring System Resume Performance on Windows Vista."&amp;nbsp; &lt;/p&gt; &lt;p&gt;First, let's go over how to handle IRP_MN_SET_POWER/SystemPowerState for PowerSystemWorking (aka S0) IRP.&amp;nbsp; You are supposed to pend the S0 irp and subsequently complete the S0 irp after you have requested a IRP_MN_SET_POWER/DevicePowerState PowerDeviceD0 IRP and it has been processed by the entire stack (which means you must register a Po completion routine which is where you complete the S0 IRP).&amp;nbsp; In short, this means that the S0 IRP is pended in your driver as long as it takes for your device to power up.&amp;nbsp; The definition of fast resume is simply that instead of pending the S0 IRP until the D0 IRP completes, you do not pend the S0 IRP and let it complete before the D0 IRP has been processed in your stack.&amp;nbsp; &lt;/p&gt; &lt;p&gt;Sounds simple, right?&amp;nbsp; Well, the devil is in the details.&amp;nbsp; Here are a couple of issues with fast resume that I have encountered:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Device power IRPs are not synchronized with PnP state changing IRPs as I wrote &lt;a href="http://blogs.msdn.com/doronh/archive/2007/02/06/which-pnp-and-power-irps-are-synchronized-against-each-other.aspx" target="_blank"&gt;about&lt;/a&gt; earlier this year.&amp;nbsp; This has a direct side effect on a fast resume implementation.&amp;nbsp; Previous to fast resume, your D0 IRP could never be concurrently&amp;nbsp;processed while a PnP state changing IRP (like a surprise remove) was being processed.&amp;nbsp; This&amp;nbsp;is due to the fact that system power IRPs are synchronized against PnP state changing IRPs and by pending the S0 IRP, you guard your device power up path from running concurrently with a PnP state change.&amp;nbsp; This made life simple for both PnP and power IRP handling in your driver, but with fast resume this is no longer true.&amp;nbsp; Now&amp;nbsp;the state changing PnP can be running concurrently with your power up path and you must make sure that the PnP dispatch routine does not assume the device's power state nor does it free resources that the power dispatch path may be using.&lt;/li&gt; &lt;li&gt;If this is a bus driver, the "free" power state synchronization between your parent device and your enumerated children goes away.&amp;nbsp;&amp;nbsp;WDM dictates that for a child device to be powered on, the parent must be powered on.&amp;nbsp; By default you can get the right behavior, only after the parent device's S0 IRP has completed back to the power manager will the power manager send S0 IRPs to your child devices.&amp;nbsp; This means that if you complete the S0 IRP before processing the D0 irp in the parent device, a new race condition occurs.&amp;nbsp; The children could receive their S0 IRP and request their D0 IRP before the parent has processed (or even requested!)&amp;nbsp;its own D0 IRP.&amp;nbsp; This can be handled in the bus driver by pending each child's D0 IRP until the parent has completed its D0 IRP, but it can get complicated quickly (especially in the face of failure to power up).&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;So given these issues, why would you even want to implement fast resume?&amp;nbsp; The answer lies in the history of fast resume, or rather, Windows 2000 which did not have this feature. Resume times on Windows 2000 laptops was not great.&amp;nbsp; There were a causes for the poor resume time, but a large contributor was device resume time.&amp;nbsp; Since the OS serialized S0 IRPs to all devices in the PnP tree, the time for each device to resume added sequentially to the system's resume time.&amp;nbsp; The OS serialization of the S0 IRPs could not change for Windows XP, so the problem was attacked at the other end...each driver would complete the S0 IRP as fast as it could so the OS could resume quickly and then asynchronously power up the device.&amp;nbsp; This way each device could power up in parallel and the total time to power up was not sequential (nor was it only the longest of any device to power up since there is still ordering between parent and child devices) and resume time could be dramatically slower.&lt;/p&gt; &lt;p&gt;Given the perceivable value you are giving to the user by implementing fast resume, I recommend that you implement it in your (non bus) function driver and handle the new races between PnP state changing IRPs and power IRPs (which if you implemented idle during S0, must also be accounted for already.&amp;nbsp; Considering the complicated aspects of asynchronously managing a parent's power state and a child's power state, I would not recommend that you implement fast resume in a bus driver. This follows the recommendations in the aforementioned white paper.&lt;/p&gt; &lt;p&gt;Obligatory KMDF plug:&amp;nbsp; KMDF automatically implements fast resume for you and handles both issues behind the scenes.&amp;nbsp; You will not process a pnp state changing irp while power up (or down) and if you are a bus driver, you will get an implementation which maintains the parent/child relationship with respect to power state.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5462922" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Vista IO manager changes in handling FILE_DEVICE_SECURE_OPEN</title><link>http://blogs.msdn.com/doronh/archive/2007/10/05/vista-io-manager-changes-in-handling-file-device-secure-open.aspx</link><pubDate>Fri, 05 Oct 2007 18:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5280017</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/5280017.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=5280017</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=5280017</wfw:comment><description>&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;After having the IO manager developer review my last 2 posts, he pointed out to me that the IO manager handling of FILE_DEVICE_SECURE_OPEN (FDSO) has changed slightly in Vista.&amp;nbsp; News to me and probably news to all of you as well.&amp;nbsp;&amp;nbsp; The change involves the case where there is a file system mounted on a device object, or in other words, a device object which implements a namespace with no file system attached is not affected by the change.&amp;nbsp; The old behavior (as I described &lt;/FONT&gt;&lt;A class="" href="http://blogs.msdn.com/doronh/archive/2007/10/04/making-sure-the-io-manager-evaluates-the-security-of-your-device.aspx" mce_href="http://blogs.msdn.com/doronh/archive/2007/10/04/making-sure-the-io-manager-evaluates-the-security-of-your-device.aspx"&gt;&lt;FONT face=Calibri color=#0000ff size=3&gt;earlier&lt;/FONT&gt;&lt;/A&gt;&lt;FONT face=Calibri size=3&gt;) skipped the security descriptor check on the device object if there was a path remaining after parsing for the device object name.&amp;nbsp; In new Vista behavior, if the device object has the FDSO flag set, has a mounted file system, and its descriptor is not the OS default descriptor, the device’s descriptor will now be evaluated, whereas previous to Vista, it would have been skipped.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: #c00000"&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: #c00000"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Why make this change?&amp;nbsp; It was driven by a usage scenario for removable devices.&amp;nbsp; The driver which enumerates the removable media volume can now alter the security descriptor and apply volume wide security settings regardless of the actual file system mounted on the drive.&amp;nbsp; For instance, the volume device could be assigned a security descriptor which only allowed administrators to write to the volume or make the entire volume read only.&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5280017" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Making sure the IO manager evaluates the security of your device</title><link>http://blogs.msdn.com/doronh/archive/2007/10/04/making-sure-the-io-manager-evaluates-the-security-of-your-device.aspx</link><pubDate>Thu, 04 Oct 2007 19:00:11 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5268065</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/5268065.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=5268065</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=5268065</wfw:comment><description>&lt;p&gt;Last time I wrote about how the &lt;a href="http://blogs.msdn.com/doronh/archive/2007/10/03/devices-and-namespaces.aspx" target="_blank"&gt;IO manager handles the creation of file handles&lt;/a&gt;&amp;nbsp;and pointed out a potential security hole.&amp;nbsp; If there is a namespace (or path) after your device's name in the path passed to CreateFile, the IO managed does not evaluate the security settings set on your device and relies on&amp;nbsp;your driver to do the evaluation.&amp;nbsp;&amp;nbsp;&amp;nbsp;For instance, if you create a device object &lt;em&gt;and &lt;/em&gt;specify that only administrators have access to it &lt;em&gt;and&lt;/em&gt;&amp;nbsp;you do not validate access during IRP_MJ_CREATE processing, any user regardless of privilege level may open a handle to your device if they specify any namespace (i.e. &lt;font face="Courier New" size="2"&gt;"\Device\FooDevice&lt;font color="#ff0000"&gt;&lt;u&gt;\Blah"&lt;/u&gt;&lt;/font&gt;&lt;/font&gt;) when opening a handle.&amp;nbsp; &lt;/p&gt; &lt;p&gt;A gut shot reaction to this behavior is that is a very large tax for every device driver to pay and the NT kernel developers agreed.&amp;nbsp; To&amp;nbsp;rectify the situation a new characteristics flag, FILE_DEVICE_SECURE_OPEN,&amp;nbsp;was added.&amp;nbsp; This flag tells the IO manager that when a device namespace is present during handle creation that the IO manager should no longer skip the security check and &lt;em&gt;&lt;strong&gt;should still&lt;/strong&gt; &lt;/em&gt;evaluate if the caller has sufficient rights to open the device.&amp;nbsp; By setting this flag, the caller's access to the object is always evaluated at the IO manager level and the driver does nothing more then set this flag.&amp;nbsp; &lt;/p&gt; &lt;p&gt;You might say to yourself, "well that is backwards, shouldn't the driver writer get this behavior by default and choose to &lt;em&gt;&lt;strong&gt;opt out &lt;/strong&gt;&lt;/em&gt;of it instead of knowing that the device must opt in to get the most secure behavior?" and I would agree with you.&amp;nbsp; The reason the most secure behavior was not made default was for backwards compatibility.&amp;nbsp; This hole was not discovered until after NT 3.1 shipped and if the IO manager behavior was flipped, already shipping drivers would stop functioning.&lt;/p&gt; &lt;p&gt;So when should you set this flag?&amp;nbsp; In my opinion you should always set this flag in a WDM driver!&amp;nbsp; The one exception is&amp;nbsp;if you are creating a device upon which a file system will be mounted (if you set the flag in this case, the file system will not be able to evaluate the security of the namespace string).&amp;nbsp; In the opinion of the WDF team, this flag is so important that we tried to guarantee that this flag is set for all WDF device objects, even if you tried to clear it by calling &lt;a href="http://msdn2.microsoft.com/en-us/library/aa491075.aspx" target="_blank"&gt;WdfDeviceInitSetCharacteristics&lt;/a&gt; or &lt;a href="http://msdn2.microsoft.com/en-us/library/aa491095.aspx" target="_blank"&gt;WdfDeviceSetCharacteristics&lt;/a&gt;.&amp;nbsp; If you are writing a KMDF driver which will have a file system mounted on top of it, this is how you would clear the flag after creating the WDFDEVICE&lt;/p&gt;&lt;pre&gt;WdfDeviceWdmGetDeviceObject(device)-&amp;gt;Characteristics &amp;amp;= ~FILE_DEVICE_SECURE_OPEN;
&lt;/pre&gt;
&lt;p&gt;This &lt;a href="http://support.microsoft.com/kb/243405" target="_blank"&gt;KB article&lt;/a&gt;&amp;nbsp;also discusses the side effects of the FILE_DEVICE_SECURE_OPEN flag on inbox drivers in previous OS releases.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5268065" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category><category domain="http://blogs.msdn.com/doronh/archive/tags/WDF/default.aspx">WDF</category></item><item><title>Devices and namespaces (or how the IO manager handles file creation)</title><link>http://blogs.msdn.com/doronh/archive/2007/10/03/devices-and-namespaces.aspx</link><pubDate>Wed, 03 Oct 2007 21:55:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5266532</guid><dc:creator>doronh</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/doronh/comments/5266532.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=5266532</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=5266532</wfw:comment><description>&lt;P&gt;Ever wonder how the creation of a handle works?&amp;nbsp; It doesn't matter type of resource the handle you are opening is backed by (a COM port, a file, a network share, a custom piece of hardware, etc), it all goes through &lt;A href="http://msdn2.microsoft.com/en-us/library/aa363858.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa363858.aspx"&gt;CreateFile&lt;/A&gt;&amp;nbsp;(which should be a little obvious since&amp;nbsp;the only way to open an type of&amp;nbsp;handle is by calling it&amp;nbsp;(with the exceptions like &lt;A href="http://msdn2.microsoft.com/en-us/library/aa363859.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa363859.aspx"&gt;this function&lt;/A&gt;&amp;nbsp; or &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/aa365150.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa365150.aspx"&gt;this function&lt;/A&gt;)).&amp;nbsp; The IO manager creates a file handle for any resource type the same way.&amp;nbsp; In theory, the way the IO manager handles file creation should be transparent to most drivers, but in reality a driver writer should&amp;nbsp;understand how it works so that you can configure your device properly&amp;nbsp;and implement &lt;A href="http://msdn2.microsoft.com/en-us/library/ms806162.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms806162.aspx"&gt;IRP_MJ_CREATE&lt;/A&gt;&amp;nbsp;properly.&amp;nbsp;&amp;nbsp; &lt;/P&gt;
&lt;P&gt;The key concept to understand here is that the IO manager does not have hard coded knowledge of each of these resources, it is completely generic.&amp;nbsp; This means that the IO manager does some parsing/path evaluation and then relies on the driver to perform some additional optional path evaluation.&amp;nbsp; Very broadly, here is what the IO manager does&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;It tries to find the device name in the lpFileName parameter.&amp;nbsp; More than likely the path will contain a symbolic link to the device name instead of the device name itself.&amp;nbsp; This means that the IO manager will evaluate the symbolic link to find the target string and then restart the parse.&lt;/LI&gt;
&lt;LI&gt;Once the device name is found, it used to find the device object (i.e. a PDEVICE_OBJECT). &lt;/LI&gt;
&lt;LI&gt;If there is no remaining path name after the device name/symbolic link, use the device object's security descriptor to evaluate if the caller has sufficient rights to open the device object.&amp;nbsp; If there is a remaining path, no security check occurs (*)!&lt;/LI&gt;
&lt;LI&gt;Create a file object, set the FileName&amp;nbsp;to the remaining path and send a IRP_MJ_CREATE&amp;nbsp;IRP to the device&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;That's it.&amp;nbsp; The FileName is the important part here.&amp;nbsp; This is how the driver provides additional path evaluation.&amp;nbsp; It is up to the driver to evaluate the FileName properly and apply its own security checks based on the string.&amp;nbsp; In WDM terms, the FileName value is a part of the device's namespace. Each device defines its own namespace, the WDK topic "&lt;A href="http://msdn2.microsoft.com/en-us/library/ms794707.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms794707.aspx"&gt;Controlling Device Namespace Access&lt;/A&gt;" explains device namespaces and does a decent job, but without the context of how the IO manager works, it is a bit indecipherable.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Let's look at a few sample file names and see how they would parse and the consequences of each example&lt;/P&gt;
&lt;TABLE class="" cellSpacing=0 cellPadding=2 width=655 border=2 unselectable="on"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=156&gt;lpFileName&lt;/TD&gt;
&lt;TD class="" vAlign=top width=203&gt;Device name&lt;/TD&gt;
&lt;TD class="" vAlign=top width=133&gt;FileName&lt;/TD&gt;
&lt;TD class="" vAlign=top width=159&gt;Security access check in IO manager&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=163&gt;&lt;FONT face="Courier New" size=2&gt;"COM1"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=209&gt;&lt;FONT face="Courier New" size=2&gt;"\Device\Serial0"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=139&gt;&lt;FONT face="Courier New" size=2&gt;NULL&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=157&gt;Yes&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=167&gt;&lt;FONT face="Courier New" size=2&gt;"COM1\Foo"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=212&gt;&lt;FONT face="Courier New" size=2&gt;"\Device\Serial0"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=143&gt;&lt;FONT face="Courier New" size=2&gt;"\Foo"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=152&gt;No&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=169&gt;&lt;FONT face="Courier New" size=2&gt;"C:\Windows\win.ini"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=213&gt;&lt;FONT face="Courier New" size=2&gt;"\Device\HarddiskVolume1"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=146&gt;&lt;FONT face="Courier New" size=2&gt;"\Windows\win.ini"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=149&gt;No&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=170&gt;&lt;FONT face="Courier New" size=2&gt;"C:"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=213&gt;&lt;FONT face="Courier New" size=2&gt;"\Device\HarddiskVolume1"&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=148&gt;&lt;FONT face="Courier New" size=2&gt;NULL&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=148&gt;Yes&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;The &lt;FONT face="Courier New" size=2&gt;"COM1"&lt;/FONT&gt;&amp;nbsp;case is straightforward.&amp;nbsp; The COM port is being opened, the IO manager evaluates whether the caller has access and it is done, but why &lt;FONT face="Courier New" size=2&gt;"COM1\Foo"&lt;/FONT&gt;&amp;nbsp;? I want to illustrate that you can append a path name to any symbolic link that you want and that it might result in failure, different behavior or be completely ignored (it will nearly always be ignored).&amp;nbsp;&amp;nbsp; When opening &lt;FONT face="Courier New" size=2&gt;"COM1\Foo", &lt;/FONT&gt;the IO manager will pass &lt;FONT face="Courier New" size=2&gt;"\Foo"&lt;/FONT&gt; to the serial driver and it will be subsequently ignored.&amp;nbsp; The path name can have meaning though as I wrote about earlier if you want to distinguish &lt;A href="http://blogs.msdn.com/doronh/archive/2006/08/18/706717.aspx" target=_blank mce_href="http://blogs.msdn.com/doronh/archive/2006/08/18/706717.aspx"&gt;which device interface is being opened&lt;/A&gt;.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;"C:\Windows\win.ini"&lt;/FONT&gt; is what many consider to the normal usage for CreateFile,&amp;nbsp;a file is being opened.&amp;nbsp; &lt;FONT face="Courier New" size=2&gt;"C:"&lt;/FONT&gt; is evaluated to particular volume and then the create is sent to the file system mounted on this volume.&amp;nbsp; The file system then parses &lt;FONT face="Courier New" size=2&gt;"\Windows\win.ini"&lt;/FONT&gt; to determine if the caller has sufficient rights to open the file.&amp;nbsp; But what about the last example, &amp;nbsp;&lt;FONT face="Courier New" size=2&gt;"C:"&lt;/FONT&gt;?&amp;nbsp; This path name (without the root directory &lt;FONT face="Courier New" size=2&gt;"\"&lt;/FONT&gt;) is asking for raw access to the volume itself (allowing you to read and write sectors directly).&amp;nbsp; &lt;/P&gt;
&lt;P&gt;I used these last 2 examples to show why the IO manager would skip the security check (step #3) if there is any remaining path.&amp;nbsp; Access to &lt;FONT face="Courier New" size=2&gt;"C:"&lt;/FONT&gt; itself requires administrative privileges while access to a file on C: may not have any security at all.&amp;nbsp; Both the volume and file system must participate in evaluating whether the current caller has sufficient rights to open a particular file handle depending on its path.&amp;nbsp; But this also brings up a very nasty security hole.&amp;nbsp; I can&amp;nbsp;append &lt;EM&gt;any &lt;/EM&gt;path to a non file system device name, cause the IO manager to skip the security check, and if the driver does not do its own security check (which it probably doesn't since it is relying on the IO manager to do it), gain access to the device regardless of the device's security descriptor.&amp;nbsp; This is a very valid concern and I will cover how to handle this in my next post.&lt;/P&gt;
&lt;P&gt;(*) Actually, it is not always skipped, but that is the next topic ;).&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5266532" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Creating symbolic links for an anonymous FDO or filter</title><link>http://blogs.msdn.com/doronh/archive/2007/05/03/creating-symbolic-links-for-an-anonymous-fdo-or-filter.aspx</link><pubDate>Thu, 03 May 2007 20:21:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2397179</guid><dc:creator>doronh</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/doronh/comments/2397179.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=2397179</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=2397179</wfw:comment><description>&lt;P&gt;As I wrote about previously, &lt;A href="http://blogs.msdn.com/doronh/archive/2007/04/18/having-two-names-is-not-necessarily-better-than-one.aspx" target=_blank mce_href="http://blogs.msdn.com/doronh/archive/2007/04/18/having-two-names-is-not-necessarily-better-than-one.aspx"&gt;naming your FDO&lt;/A&gt; has some side effects that you may not want to incur. But what if you want to give your device a fixed symbolic link name (by calling &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490622.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa490622.aspx"&gt;IoCreateSymbolicLink&lt;/A&gt;), such as \DosDevices\Foo1, in addition to your device interface GUID? Does an unnamed FDO (or filter exclude the possibility of creating the fixed symbolic link? The short answer is that it is still possible to create a fixed symbolic link for any unnamed device in a PnP stack, you just have a little more work ahead of you if you are writing a WDM driver (KMDF does implements the recommendation in this post for you). First, let's take a look at how you would create a fixed symbolic link name if your had a named device &lt;/P&gt;&lt;PRE&gt;DECLARE_CONST_UNICODE_STRING(deviceName, L"\\Device\\Foo1");
DECLARE_CONST_UNICODE_STRING(symbolicLinkName, L"\\DosDevices\Foo1");

IoCreateDevice(..., &amp;amp;deviceName, ...);
IoCreateSymbolicLinkName(&amp;amp;symbolicLinkName, &amp;amp;deviceName);
&lt;/PRE&gt;
&lt;P&gt;The key takeaway here is that the symbolic link name points to some device's name. If you had an unnamed device object, you have no device name to pass to IoCreateSymbolicLink. The saving grace here is that at least one device in the stack &lt;EM&gt;is guaranteed&lt;/EM&gt; to have a name, the PDO. All you need to do is to get the name of the PDO and create a symbolic link against the PDO's name. &lt;A href="http://msdn2.microsoft.com/en-us/library/ms801223.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms801223.aspx"&gt;IoGetDeviceProperty&lt;/A&gt;(DevicePropertyPhysicalDeviceObjectName) will give us the PDO name (as would &lt;A href="http://msdn2.microsoft.com/en-us/library/ms796477.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms796477.aspx"&gt;ObQueryNameString&lt;/A&gt;, but only until recently this was only documented in the IFS kit). To retrieve the name you must call IoGetDeviceProperty twice, first to get the length of the name buffer and second to retrieve the name. Here is how you would retrieve the PDO name and create the symbolic link against it (feel free to reuse the code in your own driver). &lt;/P&gt;&lt;PRE&gt;PDEVICE_OBJECT pPdo;
ULONG length;
NTSTATUS status;
PWSTR pName;
UNICODE_STRING pdoName;

pPdo = DeviceExtension-&amp;gt;PhysicalDeviceObject;
length = 0;

status = IoGetDeviceProperty(pPdo,
                             DevicePropertyPhysicalDeviceObjectName,
                             0,
                             NULL,
                             &amp;amp;length);

if (status != STATUS_BUFFER_TOO_SMALL &amp;amp;&amp;amp; !NT_SUCCESS(status)) {
    return status;
}
else if (length &amp;gt; USHORT_MAX) {
    return STATUS_INTEGER_OVERFLOW;
}
else if (length == 0) {
    //
    // We can get zero back if the PDO is being deleted.  This can only happen if we are 
    // creating the symbolic link outside of AddDevice() or a PnP start.
    //
    return STATUS_INVALID_DEVICE_STATE;
}

pName = (PWSTR) ExAllocatePoolWithTag(PagedPool, length, &lt;I&gt;[tag]&lt;/I&gt;);
if (pName == NULL) {
    return STATUS_INSUFFICIENT_RESOURCES;
}

status = IoGetDeviceProperty(pPdo,
                             DevicePropertyPhysicalDeviceObjectName,
                             length,
                             pName,
                             &amp;amp;length);

if (!NT_SUCCESS(status)) {
    ExFreePool(pName);
    pName = NULL;
    return status;
}

pdoName.Buffer = pName;
pdoName.Length = (USHORT) length - sizeof(UNICODE_NULL);
pdoName.MaximumLength = (USHORT) length;

status = IoCreateSymbolicLink(&lt;I&gt;[SymbolicLinkName]&lt;/I&gt;, &amp;amp;pdoName)

if (NT_SUCCESS(status)) {
    // the type of DeviceExtension-&amp;gt;PdoName is UNICODE_STRING
    RtlCopyMemory(&amp;amp;DeviceExtension-&amp;gt;PdoName, &amp;amp;pdoName, sizeof(pdoName));

    // now that we stored the buffer in our extension, remove all references to the buffer
    // so that we don't accidentally free it while still pointing to it in the extension
    pName = NULL;
    RtlZeroMemory(&amp;amp;pdoName, sizeof(pdoName));
}
else {
    ExFreePool(pName);
    pName = NULL;
}

return status;
&lt;/PRE&gt;
&lt;P&gt;Remember that you must also &lt;A href="http://blogs.msdn.com/doronh/archive/2006/02/24/538790.aspx" target=_blank mce_href="http://blogs.msdn.com/doronh/archive/2006/02/24/538790.aspx"&gt;destroy the symbolic on a surprise remove&lt;/A&gt; or graceful remove which is why I stored the PDO name in the device extension. Otherwise, you would have to query, allocate and requery during remove processing, which just makes your driver more complicated (and if the alloc fails, there is no way for you to destroy the symbolic link name, so by storing it upon creation, we can guarantee we can destroy it later). Also, remember to free DeviceExtension-&amp;gt;PdoName.Buffer during remove processing when you are freeing resources! Again, KMDF does all of this for you (including the automatic destroying of the link upon deletion), all you have to do is create an unnamed WDFDEVICE and then call &lt;A href="http://msdn2.microsoft.com/en-us/library/aa491153.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa491153.aspx"&gt;WdfDeviceCreateSymbolicLink&lt;/A&gt; and you are done. &lt;/P&gt;
&lt;P&gt;If you think about this a little more deeply, this is exactly how device interfaces work. When you create a device interface, the OS creates a symbolic link name on your behalf and has it point to the PDO's name. The difference between your symbolic link name and the OS generated name based on the device interface GUID is that the device interface has built in mechanisms to notify an application of arrival or departure and has a name which can be queried for at runtime. The fixed symbolic link has no such notifications and there is no way to tell how many of these links exist. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;Special thanks to Mark Roddy who intially suggested this technique during KMDF beta testing.&lt;/EM&gt;&lt;/STRONG&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2397179" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Having two names is not necessarily better than one</title><link>http://blogs.msdn.com/doronh/archive/2007/04/18/having-two-names-is-not-necessarily-better-than-one.aspx</link><pubDate>Wed, 18 Apr 2007 23:20:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2179821</guid><dc:creator>doronh</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/doronh/comments/2179821.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=2179821</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=2179821</wfw:comment><description>&lt;P&gt;Every physical device object (PDO) &lt;A href="http://msdn2.microsoft.com/en-us/library/ms794698.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms794698.aspx"&gt;must have a name&lt;/A&gt;. Furthermore, if you read the entire MSDN page, you see that any device attached to the PDO must &lt;EM&gt;not&lt;/EM&gt; have a name. Why does such a rule exist? To answer this question, let's explore what happens if more than one device in the stack has a name. Furthermore, each named device has a symbolic link created against it so that a user mode application can easily create a file handle, as shown by this diagram&lt;/P&gt;&lt;PRE&gt;    +----------------+
    |      FDO       |  
    | \Device\Name2  | &amp;lt;-- Symbolic link \DosDevices\Name2
    +----------------+
             |
    +----------------+
    |      PDO       |
    | \Device\Name1  | &amp;lt;-- Symbolic link created by IoRegisterDeviceInterface 
    +----------------+
&lt;/PRE&gt;
&lt;P&gt;To process the CreateFile request, the I/O manager resolves the symbolic name to the underlying device name and sends the IRP_MJ_CREATE to the top of the stack. If the create succeeds, all I/O flows through the top of the stack and it is up to the stack to forward or process I/O requests as appropriate. This means that no matter which name (\DosDevices\Name2 or the device interface name returned by SetupAPI) you used to open the device stack, the I/O will always be sent to the top. It is not as if you can open the stack by the PDO's name and send I/O to the PDO directly without the FDO seeing the I/O requests. So, why are having additional names a problem in this scenario? The answer is that this is not the reason that this rule exists. &lt;/P&gt;
&lt;P&gt;So, again, why does this rule exist? The rule exists because of how the I/O manager uses the device object derived from the device name/symbolic when creating a file handle. Each device has its own set of properties (exclusive, secure open, security descriptor) that are specified when the device is created by calling &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490468.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/aa490468.aspx"&gt;IoCreateDevice&lt;/A&gt; or &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490540.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa490540.aspx"&gt;IoCreateDeviceSecure&lt;/A&gt; and uses the properties associated with only this device object and not any other device object in the stack. This means that if the PDO has a security descriptor that allows access only to user A and excludes B, while the FDO has a security descriptor that allows access only to user B, user B can open the stack by using the FDO's name and circumvent the PDO's security settings. In other words, you have a potential security hole! Since each name is evaluated on its own, every named device in the stack can be opened with their own set of rights and properties. &lt;/P&gt;
&lt;P&gt;This leads to 2 more questions. &lt;/P&gt;
&lt;H3&gt;How do I specify the properties (exclusivity, security, etc) I want for the stack if I can't have a name and some other driver is creating the PDO? &lt;/H3&gt;
&lt;P&gt;This is a legitimate problem. If your driver has no control over how the PDO is created, how can you affect its settings? Or to turn it around, how is the bus driver going to know what settings are appropriate for the PDO it is creating? Most bus drivers do not know what specific driver will be loaded on the PDO it is enumerating. To fix this, the PnP manager will adjust the settings of each device object in the stack to a specific value. These values are written to the device node during installation by the INF. You use the hardware &lt;A href="http://msdn2.microsoft.com/en-us/library/ms794514.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms794514.aspx"&gt;AddReg directive&lt;/A&gt; to tell the PnP manager what to set for the stack. Using the INF, you can set stack wide settings for device characteristics, device type, security, and exclusivity. &lt;/P&gt;
&lt;H3&gt;Why do some stacks have named device objects in their stacks that are not the PDO? &lt;/H3&gt;
&lt;P&gt;Some stacks have named FDOs or filters because of legacy reasons that predate PnP. For instance, the storage stacks name their device objects because other system components expect to find these names. In these stacks which have multiple device objects with a name, you can still use the INF directives to create stack wide settings. &lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2179821" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Problems with not having a current IRP stack location, part 2</title><link>http://blogs.msdn.com/doronh/archive/2007/04/09/problems-with-not-having-a-current-irp-stack-location-part-2.aspx</link><pubDate>Mon, 09 Apr 2007 19:04:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2042558</guid><dc:creator>doronh</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/doronh/comments/2042558.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=2042558</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=2042558</wfw:comment><description>&lt;P&gt;This problem falls into the category being hidden by a macro that does not indicate in its name what it touches. If you call &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490530.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa490530.aspx"&gt;IoMarkIrpPending&lt;/A&gt; on an IRP that you allocated in your driver, chances are that you are corrupting memory. First, let's look at the implementation of this function (from wdm.h): &lt;/P&gt;&lt;PRE&gt;#define IoMarkIrpPending( Irp ) ( \
    IoGetCurrentIrpStackLocation( (Irp) )-&amp;gt;Control |= SL_PENDING_RETURNED )
&lt;/PRE&gt;
&lt;P&gt;This function retrieves the current stack location and sets a flag in one of the fields (IO_STACK_LOCATION::Control). Since the current IRP stack location is not valid, and by not valid I mean that the pointer value points to undefined memory, not that it is NULL, so the call corrupts memory (a nefarious single bit flip) somewhere in the system. It so happens that based on how IRPs are currently allocated today, you will corrupt memory immediately after the IRP allocation making this a little bit easier to diagnose later. &lt;/P&gt;
&lt;P&gt;How would a mistaken call to IoMarkIrpPending even get into your driver? Well, most code is copied from somewhere else. A lot of completion routines have the following code in them. These completion routines were written to manipulate an I/O manager presented IRP (which works because there is a current stack location), but when copied over they have a time bomb waiting to go off in them. Here is an example completion routine which is copied:&lt;/P&gt;&lt;PRE&gt;NTSTATUS CompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
{
    ...
    if (Irp-&amp;gt;PendingReturned) {
         IoMarkIrpPending(Irp);
    }
    ...
    IoFreeIrp(Irp);

    //
    // Must return STATUS_MORE_PROCESSING_REQUIRED because we cannot let the IRP complete
    // back to the I/O manager
    //
    return STATUS_MORE_PROCESSING_REQUIRED;
}
&lt;/PRE&gt;
&lt;P&gt;What happens if you have an I/O completion routine that handles both types (driver created, I/O manager presented) IRPs? In the I/O manager presented IRP case you need to propagate the pending state by calling IoMarkIrpPending, while in the driver create case you obviously should not propagate the state. KMDF must do this since it sets the same completion routine for all requests sent to a &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490261.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa490261.aspx"&gt;WDFIOTARGET&lt;/A&gt;. Here is how KMDF manages this functionality. If Irp-&amp;gt;PendingReturned is TRUE and the current stack location count is less than or equal to the total stack count (in the driver allocated case, CurrentLocation will be &amp;gt; StackCount), propagate the pending into the current stack. &lt;/P&gt;&lt;PRE&gt;VOID
PropagatePendingReturned(
    PIRP Irp
    )
{
   if (Irp-&amp;gt;PendingReturned &amp;amp;&amp;amp; Irp-&amp;gt;CurrentLocation &amp;lt;= Irp-&amp;gt;StackCount) {
       IoMarkIrpPending(Irp);
   }
}
&lt;/PRE&gt;
&lt;P&gt;BTW, unaccredited &lt;A href="http://blogs.msdn.com/doronh/archive/2006/07/25/677310.aspx" target=_blank mce_href="http://blogs.msdn.com/doronh/archive/2006/07/25/677310.aspx"&gt;part 1&lt;/A&gt; is why you don't have a DeviceObject in your I/O completion routine&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2042558" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/KMDF/default.aspx">KMDF</category><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item><item><title>Your completion routine context can be anything you want</title><link>http://blogs.msdn.com/doronh/archive/2007/04/06/your-completion-routine-context-can-be-anything-you-want.aspx</link><pubDate>Sat, 07 Apr 2007 00:25:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2042133</guid><dc:creator>doronh</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/doronh/comments/2042133.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=2042133</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=2042133</wfw:comment><description>&lt;P&gt;It sounds obvious, but sometimes it needs to be stated. For instance, let's say that you are allocating your own IRP, your context contains I/O related data (like a &lt;A href="http://msdn2.microsoft.com/en-us/library/ms793340.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms793340.aspx"&gt;URB&lt;/A&gt;) and you encounter the issue where the DeviceObject passed to your I/O completion routine is NULL. &lt;A href="http://blogs.msdn.com/doronh/archive/2006/07/25/677310.aspx" target=_blank mce_href="http://blogs.msdn.com/doronh/archive/2006/07/25/677310.aspx"&gt;Adding another stack location is one solution&lt;/A&gt; I wrote about, but it is a little complicated and has a certain black magic feeling to it, besides it does not work with any IoBuildXxx API calls. Since you are allocating memory for your context, just allocate a little bit more. Instead of allocating just the URB &lt;/P&gt;&lt;PRE&gt;PURB urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), ...);
PIRP irp = IoAllocateIrp(...);
...
// format the URB
IoSetCompletionRoutine(Irp, CompletionRoutine, urb, TRUE, TRUE, TRUE);
...
IoCallDriver(..., Irp);
...

NTSTATUS CompletionRoutine(PDEVIC_OBJECT Device, PIRP Irp, PURB Urb)
{
     // Do not touch Device, it is NULL!
     // Process Urb and Irp results
     ExFreePool(Urb);
     IoFreeIrp(Irp);

     return STATUS_MORE_PROCESSING_REQUIRED;
}
&lt;/PRE&gt;
&lt;P&gt;allocate a structure to contain the URB and your DeviceObject pointer (or WDFDEVICE or anything else) &lt;/P&gt;&lt;PRE&gt;typedef struct _COMPLETION_DATA {
    PDEVICE_OBJECT DeviceObject;
    URB Urb;
} COMPLETION_DATA, *PCOMPLETION_DATA;

PCOMPLETION_DATA data = ExAllocatePoolWithTag(NonPagedPool, sizeof(COMPLETION_DATA), ...);
PIRP irp = IoAllocateIrp(...);
...
// format data-&amp;gt;Urb
data-&amp;gt;DeviceObject = DeviceObject;
IoSetCompletionRoutine(Irp, CompletionRoutine, data, TRUE, TRUE, TRUE);
...
IoCallDriver(..., Irp);
...

NTSTATUS CompletionRoutine(PDEVIC_OBJECT Device, PIRP Irp, PCOMPLETION_DATA Data)
{
     // Do not touch Device, it is NULL, but Data-&amp;gt;Device is valid!
     
     // Process Data-&amp;gt;Urb and Irp results
     ExFreePool(Data);
     IoFreeIrp(Irp);

     return STATUS_MORE_PROCESSING_REQUIRED;
}
&lt;/PRE&gt;
&lt;P&gt;Like I said, it is an obvious thing to do :), but when you are in the depths of debugging a bugcheck, sometimes the obvious solution is the last thing you think of.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2042133" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category><category domain="http://blogs.msdn.com/doronh/archive/tags/WDF/default.aspx">WDF</category></item><item><title>Filtering HID collections and setting I/O flags</title><link>http://blogs.msdn.com/doronh/archive/2007/03/26/filtering-hid-collections-and-setting-i-o-flags.aspx</link><pubDate>Tue, 27 Mar 2007 03:19:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1956548</guid><dc:creator>doronh</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/doronh/comments/1956548.aspx</comments><wfw:commentRss>http://blogs.msdn.com/doronh/commentrss.aspx?PostID=1956548</wfw:commentRss><wfw:comment>http://blogs.msdn.com/doronh/rsscomments.aspx?PostID=1956548</wfw:comment><description>&lt;P&gt;Over the past 3 years or so, I have been casually referring to KMDF as the ultimate driver compat layer. Just like Windows has an application compatibility layer which shields applications from OS changes, KMDF provides the same type of compatibility across device stacks. For the most part each stack implements WDM as per the documentation and there is nothing special that KMDF must compensate for when inserted into the stack. HID is not one of these stacks ;). First, let me describe the HID stack. &lt;/P&gt;&lt;PRE&gt;+---------+
| Raw PDO |
+---------+
     |
     |    +----------------+
     +----| HID miniport + |
          | HIDclass       | 
          +----------------+
                   |
          +----------------+
          |       PDO      |
          +----------------+
&lt;/PRE&gt;
&lt;P&gt;HIDClass (the port driver) will enumerate a raw PDO for each top level collection (TLC) in a device's HID descriptor (in this picture there is only one raw PDO, but there could be many). A raw PDO is special in that it does not need an additional INF to be started, all of the functionality that is usually found in a stack's FDO is in the raw PDO. This includes the DeviceObject Flags which specify the buffering type for reads and writes on the device (more on this later). A raw PDO can have a FDO and filters layered on top of it, this is how the HID keyboard and mouse stacks work. A TLC which is neither a keyboard nor a mouse can have a filter installed on it as well. &lt;/P&gt;
&lt;P&gt;Setting the flags is where HID does not play by the rules. Typically a bus driver will specify the flags (DO_DIRECT_IO or DO_BUFFERED_IO) which dictate the buffering in read and write IRPs when the device object is created. This would allow an FDO or filter to determine the I/O type during the FDO or filter's AddDevice routine and copy them into its own device object (since the I/O manager looks at the top of the stack to determine buffering for reads and writes). HIDClass on the other hand sets one of the flag (DO_DIRECT_IO) during IRP_MN_START_DEVICE! This means that in a WDM driver filtering on top of&amp;nbsp;a raw HID PDO, you must special case this behavior and set these flags in your device object during IRP_MN_START_DEVICE processing. KMDF takes care of this for you, so your HID filter code is no different than a filter for any other device stack (this is where the driver compat layer is). &lt;/P&gt;
&lt;P&gt;What makes the situation worse is that there is a HID filter sample, firefly, which does not change the I/O type in its device object's Flags, meaning it is now a generic HID filter sample, rather only a HID mouse sample.&amp;nbsp; But how does firefly even work in the mouse stack? Well, the upper edge of the mouse stack (mouclass.sys) always sets DO_BUFFERED_IO and read IRPs are never sent down the stack, so HIDClass would never see a read IRP with the wrong buffer type. This means that the propagation of the buffering flags is not needed.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;So how is it that this bug existed HIDClass for so long without being discovered? Remember that HIDClass enumerates its PDOs as raw and 99% of the time, nobody loads on top of them, so there was no other WDM component to interact with this behavior of setting the Flags field at the wrong time. &lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1956548" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/doronh/archive/tags/KMDF/default.aspx">KMDF</category><category domain="http://blogs.msdn.com/doronh/archive/tags/WDM/default.aspx">WDM</category></item></channel></rss>