Possible causes of STATUS_DEVICE_POWER_FAILURE for a virtual display adapter?

Hi Marcel. Thanks for the reply. I’ll let you know.

Hey Marcel,

I’m checking with a few users who’ve reported the problem in our current driver. I’ll let you know if it clears out the STATUS_DEVICE_POWER_FAILURE error.

Meanwhile, I’ve been testing a refactored “v2.0” video driver, and I ran into this maddening problem where attempts to use CreateFile to open a handle to the display adapter device were failing with an “AccessDenied” error.

While investigating this, there were a few times where the test containing the CreateFile call suddenly started succeeding. At first, I assumed that last thing I’d changed in the code was responsible. Then at some future point (probably after a reboot), things would mysteriously go back to failing again. It’s been driving me crazy.

After your response, I tried disabling and re-enabling the adapter device, prior to running my test. The problem goes away until I reboot. I’ve tried this a few times, and it’s consistent.

Whatever this is, disabling/re-enabling seems to fix it temporarily. Sounds like you might be investigating this as well. Any insights as to what’s going on?

–Scott

First quick thought: These things usually happen when using non-Plug’n’Play function WdfDeviceCreateSymbolicLink instead of Plug’n’Play function WdfDeviceCreateDeviceInterface. Much more when known WDDM mechanisms (e.g. TDR) are at work.

The second thought is a bit frightening, though: Maybe these problems are resulting from using an upper edge driver interface which does not belong to you? The IddCx driver’s upper edge actually belongs to the upper filter driver sitting on top of the IddCx UMDF device: IndirectKmd.sys

There are two layers above the actual UMDF driver implementation:

  1. First the IndirectKmd.sys filter driver
  2. Second, the IddCx framework itself hides the driver’s primary WDFQueue from the developer.

Marcel Ruedinger
datronicsoft

1 Like

I’m still waiting for feedback from a user who was seeing this STATUS_DEVICE_POWER_FAILURE problem in our v1 driver.

First quick thought: These things usually happen when using non-Plug’n’Play function WdfDeviceCreateSymbolicLink instead of Plug’n’Play function WdfDeviceCreateDeviceInterface. Much more when known WDDM mechanisms (e.g. TDR) are at work.

This is a WDF driver based upon the IddCx sample from Microsoft (UMDF 2.x). I do call WdfDeviceCreateDeviceInterface in EvtDriverDeviceAdd after a successful call to WdfDeviceCreate:

ntStatus = ::WdfDeviceCreateDeviceInterface(hWdfAdapterDevice, &GUID_DEVINTERFACE_IMVD, nullptr);

Maybe these problems are resulting from using an upper edge driver interface which does not belong to you?

This interface is my own custom set of IOCTL commands that allow configuration of connected displays. There’s no actual data transfer (reading/writing), just config functions. But none of that gets called until after CreateFile succeeds:

// A custom interface GUID ({580FE7DF-137A-45F1-9998-7B1F006CDC43}) so that apps can find and communicate with this device
DEFINE_GUID(GUID_DEVINTERFACE_IMVD, 0x580fe7df, 0x137a, 0x45f1, 0x99, 0x98, 0x7b, 0x1f, 0x0, 0x6c, 0xdc, 0x43);

One interesting thing to add from my testing of the V2 driver. At one point, I waited too long in the debugger and the watchdog asserted and marked the device as not working (as expected).

I disabled and then enabled the device to reset its status to “working properly” again. Then I ran my test and the CreateFile called failed with AccessDenied.

Here’s the interesting part: I had to cycle the device (disabled + enable) again, and then the test’s CreateFile call succeeded. It’s as though the driver has to initialize successfully, and then initialize again.

Could I be making my WdfDeviceCreateDeviceInterface call at the wrong time (e.g., too early)?

It is IndirectKmd.sys and IddCx.dll who get asked first if the application’s CreateFile(…) call succeeds (and if the file is shareable, etc.). It is not really documented if their design allows application access for the lowest layer UMDF part of the driver (many kernel mode miniport driver models are NOT designed for application access).

The mere presence of the callback IddConfig.EvtIddCxDeviceIoControl could be interpreted as a positive hint. Unfortunately the purpose of this callback is not documented. It could also be intended only for legacy IOCTL_VIDEO_Xxx from the operating system. The comment in the MS WDK Sample you mentioned might be interpreted as a negative hint: “…is called by the OS when an IO control request needs processing by the driver”.

To cut a long story short: It is already a safe choice, not to convey data over this interface. Possibly you could also convey your control information in a different way?

Marcel Ruedinger
datronicsoft

1 Like

Since the interface string I get is a symlink, I used SysInternals WinObj to locate the item and see what actual device it refers to:

Name                                                        Type           Symlink
--------------------------------------------------------   ------------   ----------------
ROOT@DISPLAY#0000#{1ca05180-a699-450a-9a0c-de4fbe3ddd89}   SymbolicLink   \device\00000001   (GUID_DISPLAY_DEVICE_ARRIVAL)
ROOT@DISPLAY#0000#{1ca05181-a699-450a-9a0c-de4fbe3ddd89}   SymbolicLink   \device\00000001   (Undocumented - related to above?)
ROOT@DISPLAY#0000#{580fe7df-137a-45f1-9998-7b1f006cdc43}   SymbolicLink   \device\00000001   (GUID_DEVINTERFACE_IMVD - my custom interface)
ROOT@DISPLAY#0000#{5b45201d-f2f2-4f3b-85bb-30ff1f953599}   SymbolicLink   \device\00000001   (GUID_DEVINTERFACE_DISPLAY_ADAPTER)

I then go look up that device under the Device node in the tree, and check the permissions on that. Sure enough, if I do it before I’ve disabled/enabled the adapter instance in Device Manager (e.g., after an OS restart), I can’t view properties on the mapped device (“Error opening \Device\00000001: Access is denied.”). But once I’ve disabled/enabled the adapter, I can view the properties with no problem.

Possibly you could also convey your control information in a different way?

Any suggestions?

More info:

I stepped into the CreateFile call with WinDbg, then set a process-specific breakpoint on the kernel side.

IopParseDevice is where the Access Denied error occurs:

— User Mode —
mytest.exe
CreateFile
ntdll!NtCreateFile
— Kernel Mode —
nt:IopCreateFile
nt:ObOpenObjectByNameEx
nt:ObpLookupObjectName
nt:IopParseDevice [BANG]
(nt:IofCallDriver does not get called)

This is the definition I was able to find for IopParseDevice:

NTSTATUS
IopParseDevice(
  IN  PVOID ParseObject,
  IN  PVOID ObjectType,
  IN  PACCESS_STATE AccessState,
  IN  KPROCESSOR_MODE AccessMode,
  IN  ULONG Attributes,
  IN OUT PUNICODE_STRING CompleteName,
  IN OUT PUNICODE_STRING RemainingName,
  IN OUT PVOID Context OPTIONAL,
  IN  PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
  OUT PVOID *Object
  );

I wasn’t able to make much sense of the arguments, except for the two in/out strings (" " and “” respectively).

I’ll continue stepping through IopParseDevice in hopes of getting closer to the reason for the Access Denied result…

Only two more things left for me to say:

  1. I am currently trying to get public info from Microsoft about the purpose of the IddCX Upper-Edge Ioctl-Interface. If successful, it will probably take a few days…

  2. Coming back to the timing question above: In this kind of WDDM IddCx driver, it would defintely be the wrong time (too early) to process EvtIddCxDeviceIoControl before having received the event EvtIddCxAdapterInitFinished. Instead of calling WdfDeviceCreateDeviceInterface later, it might be more helpful using a WDF Queue which is initially stopped and started only in EvtIddCxAdapterInitFinished.

Marcel Ruedinger
datronicsoft

1 Like

Instead of calling WdfDeviceCreateDeviceInterface later, it might be more helpful using a WDF Queue which is initially stopped and started only in EvtIddCxAdapterInitFinished.

Interesting. I based my code on Windows-driver-samples\general\echo\umdf2\driver\AutoSync\queue.c…:

// Create a device interface for this virtual display adapter device
ntStatus = ::WdfDeviceCreateDeviceInterface(hWdfAdapterDevice, &Public::GUID_DEVINTERFACE_IMVD, nullptr);

if (NT_SUCCESS(ntStatus))
{
    // Create and initialize an I/O queue for the adapter device
    . . .

    // Configure a default queue so that requests that aren't configure-forwarded (using WdfDeviceConfigureRequestDispatching to
    // go to other queues) get dispatched here.
    WDF_IO_QUEUE_CONFIG config{};
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&config, WdfIoQueueDispatchSequential);
    config.PowerManaged       = WdfFalse; //WdfUseDefault TODO
    config.EvtIoDeviceControl = OnWdfIoQueueIoDeviceControl;

    // Fill in a callback for destroy, and our CQueue struct size
    WDF_OBJECT_ATTRIBUTES attributes{};
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, CQueue);
    attributes.EvtCleanupCallback = OnQueueContextCleanup;       // Called when the object is deleted

    // Set synchronization scope on queue and have the timer to use queue as the parent object so that queue and timer callbacks
    // are synchronized with the same lock.
    attributes.SynchronizationScope = WdfSynchronizationScopeQueue;

    // Create and configure an I/O queue for the adapter device
    WDFQUEUE hWdfQueueOut = nullptr;
    ntStatus = ::WdfIoQueueCreate(hWdfAdapterDevice, &config, &attributes, &hWdfQueueOut);
    . . .
}

…but the echo sample was queuing reads and writes. Once I’d boiled everything down, it seemed to me that the queue wasn’t necessary in my particular case, so I don’t create the timer.

I’m only sending (synchronous) IOCTL commands to the adapter, and they result in a call to my adapter’s EvtIddCxDeviceIoControl callback. I found that the queue’s EvtIoDeviceControl callback never got called (which makes sense, because I don’t read or write to the device).

Am I correct in my belief that the queue serves no purpose here?

Another update

I’ve been able to trace the original CreateFile call into the kernel. nt!IopParseDevice calls nt!SeAccessCheck with slightly different parameters, depending upon whether I’ve disabled/enabled my adapter device.

Notably, the provided SecurityDescriptor’s SECURITY_DESCRIPTOR_CONTROL member changes:

(after Windows restart)
0x9814 (SE_SELF_RELATIVE | SE_DACL_PROTECTED | SE_SACL_AUTO_INHERITED | SE_SACL_PRESENT | SE_DACL_PRESENT)

(after disable/enable adapter)
0x8014 (SE_SELF_RELATIVE |                                              SE_SACL_PRESENT | SE_DACL_PRESENT)

I’m running the test after restarting Windows (Access Denied), then I disable/enable the adapter and immediately run the same test again. I break on nt!SeAcccessCheck and record the parameters passed in.

I don’t know enough about Windows security stuff to understand what these two flags signify in the larger context. I also don’t know if this kind of thing is expected behavior, or part of my specific problem…

Doesn’t seem to lead any closer to a solution.
Ever tried to assign a security descriptor in the INF file (see example below)?

[MyDevice_HardwareDeviceSettings]
HKR, “UpperFilters”, %REG_MULTI_SZ%, “IndirectKmd”

HKR,Security,“D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;WD)(A;;GA;;;RC)”

Marcel Ruedinger
datronicsoft

1 Like

Doesn’t seem to lead any closer to a solution.
Ever tried to assign a security descriptor in the INF file (see example below)?

No, but I will now. Thanks. Didn’t realize it was an option…

Coming back to my previous pondering about the purpose of EvtIddCxDeviceIoControl.

There is a clear public statement from Microsoft now. WDK docs were just updated as follows (quote):

IDD_CX_CLIENT_CONFIG structure
EvtIddCxDeviceIoControl
This function can be used for communication between a user session process and the driver; typically this communication channel is used by a companion application to control some aspect of the driver.

Marcel Ruedinger
datronicsoft

1 Like

So if I’m interpreting that correctly, it says that the approach I’m taking is the intended one? It ought to work?

Correct - if you do it right, then it will work!

So what could still be wrong? Maybe your user mode Plug’n’Play implementation? Maybe DIGCF_PRESENT flag forgotten when building the set of device interfaces? Thus trying to access a device interface that is not necessarily active?

PS: Too extensive use of old (but not osolete) kernel mode debugging techniques might not be concluive here. This is a User Mode Driver. WDK docs explain various UMDF debugging techniques which might be worth a closer look…

1 Like

Maybe your user mode Plug’n’Play implementation?

I did factor Microsoft’s everything-in-a-single-source-file sample code into separate files/classes. It’s possible that I introduced some subtle change in the order of initialization steps. I’m going to compare mine against the sample (call-by-call) to see whether they’re different.

Maybe DIGCF_PRESENT flag forgotten when building the set of device interfaces?

I’m actually using CfgMgr32 APIs in my console test program, and I am specifying CM_GET_DEVICE_INTERFACE_LIST_PRESENT:

_Check_return_ //
static std::wstring GetDevicePath(REFGUID interfaceClassGuid)
{
    std::wstring result;

    // Request the minimum buffer size to contain the (NUL-delimited) list of devices that support the specified interface
    ULONG listSizeInWords = 0;
    auto  cr = ::CM_Get_Device_Interface_List_SizeW(
        &listSizeInWords,                           // Pointer to a location that receives the required length, in characters
        const_cast<LPGUID>(&interfaceClassGuid),    // GUID that identifies a device interface class
        nullptr,                                    // Device instance ID
        CM_GET_DEVICE_INTERFACE_LIST_PRESENT);      // Flags
    if (cr != CR_SUCCESS)
    {
        std::wcerr << L"\tERROR: Unable to retrieve device interface list size; CONFIGRET: " << Text::CrToString(cr).c_str();
    }
    else if (listSizeInWords <= 1)
    {
        std::wcerr << L"\tWARNING: No active device interfaces found. Is the immersed-display-driver loaded?" << std::endl;
    }
    else
    {
        // Use 'fill' ctor to preallocate (and initialize with NULs) the minimum required buffer to contain the list
        std::wstring deviceItfList(listSizeInWords, L'\0');

        // Query the (NUL-delimited, double-NUL-terminated) list of device paths
        cr = ::CM_Get_Device_Interface_ListW(
            const_cast<LPGUID>(&interfaceClassGuid),    // GUID that identifies the device interface class
            nullptr,                               // Device instance ID
            &deviceItfList[0],                     // Buffer to receive list
            listSizeInWords,                       // Length (in wchar_t), of the provided buffer
            CM_GET_DEVICE_INTERFACE_LIST_PRESENT); // Flags
        if (cr != CR_SUCCESS)
        {
            std::wcerr << L"\tERROR: Unable to retrieve device interface list; CONFIGRET: " << Text::CrToString(cr).c_str();
        }
        else
        {
            // Convert the NUL-delimited list to a vector<wstring>
            auto deviceInterfaces = Text::Split(deviceItfList.c_str());

            ASSERT(!deviceInterfaces.empty()); // We checked listSizeInWords above, so this shouldn't be empty

            // We're looking for our custom driver interface, so there should only be ONE device in the list
            if (1 < deviceInterfaces.size())
            {
                std::wcerr << L"\tWARNING: More than one device interface instance found. Selecting FIRST matching device."
                           << std::endl;
            }

            result = deviceInterfaces[0];
        }
    }

    return result;
}

Also, I’m getting the same Device Path string both before and after the disable-enable:

c:\display-driver-v3\Windows\bin\Debug>display-driver-ioctl-tester.exe

INFO: Got device path: "\\?\ROOT#DISPLAY#0000#{580fe7df-137a-45f1-9998-7b1f006cdc43}"
ERROR: Failed to open the device "\\?\ROOT#DISPLAY#0000#{580fe7df-137a-45f1-9998-7b1f006cdc43}"; Access is denied.

(here I manually disable and re-enable the driver)

c:\display-driver-v3\Windows\bin\Debug>display-driver-ioctl-tester.exe

INFO: Got device path: "\\?\ROOT#DISPLAY#0000#{580fe7df-137a-45f1-9998-7b1f006cdc43}"

(...successful output follows...)

Too extensive use of old (but not obsolete) kernel mode debugging techniques might not be conclusive here.

I was hoping that if I traced the CreateFile API to the point of failure (down in the kernel), I’d have an ‘Ah Hah!’ moment. Unfortunately, I didn’t know how to interpret the results. It was an interesting exercise, though. I ordered Windows Internals 1 & 2, edition 7 yesterday (along with some driver development books) so I can learn more about this.

This is a User Mode Driver. WDK docs explain various UMDF debugging techniques which might be worth a closer look…

I’m setting that up again so I can record the actual flow through the driver. Once I had the driver working, I’d switched back to the Visual Studio debugger for the IOCTL stuff, because it’s so much more familiar.

But now I need to break on driver load and view trace output. I will have a look at the UMDF debugging techniques in the WDK docs again to see if I missed anything, but IIRC, I need to go back to WinDbg…, WdfVerifier, TraceView, etc. for that.

Cheers.

I know less than nothing about display drivers. But…

was hoping that if I traced the CreateFile API to the point of failure (down in the kernel), I’d have an ‘Ah Hah!’ moment. Unfortunately, I didn’t know how to interpret the results.

You showed that the failure occurred in IopParseDevice (the “parse” method for DEVICE_OBJECT). This is expected, as this is the function in the I/O Manager where the Create IRP is built and sent to the driver.

At SOME point, I’d expect IopParseDevice to call SeAccessCheck. Either step through IopParseDevice (a real snoozer) or perhaps start by setting a breakpoint on SeAccessCheck?

There are two issues here: Why are you getting back access denied, and if this is related to the ACL what is the ACL set to?

I hate to ask the obvious, but have you looked at the ACL on the Device Object in question?

Don’t know if any of that is helpful. Like I said, I can barely find the power button to turn on my display device, I know so little about this particular device class.

Peter

1 Like

Thanks for jumping in Mr. Viscarola. Your advice emphasizes my suggestion to try the security descriptor in the INF file. Feedback is still pending…

Probably not much knowledge about the display device class needed any more to hang around in this thread.
We have an OS supplied function driver (WUDFRd.sys) together with an OS supplied upper filter (IndirectKmd.sys). The chances of finding any problem with these OS supplied drivers is magnitudes smaller than pinpointing the problem either at the application code or the UMDF driver DLL code (e.g. something dangling). Hence my comment about kernel mode debugging (for those who didn’t notice: quoting a line from a very popular 2015 movie “Old but not obsolete!”).

Two potential outcomes:

  1. Bug or potential usage problem with OS supplied display drivers:
    Very small probability.

  2. Needle in the haystack of developer’s code:
    Very high probability.

How far can/shall we proceed here?

Marcel Ruedinger
datronicsoft

1 Like

Hi Peter,

Thanks for chiming in!

…this is the function in the I/O Manager where the Create IRP is built and sent to the driver.
At SOME point, I’d expect IopParseDevice to call SeAccessCheck.

Yes indeed. I was able to determine that SeAccessCheck was returning a different result depending on whether or not I’d cycled the enabled state of the device.

Either step through IopParseDevice (a real snoozer) or perhaps start by setting a breakpoint on SeAccessCheck?

I did both. WinDbg is only showing me the assembly, with symbols. I know enough about the calling/return convention that I was able to painstakingly figure out what the arguments/returns were from IopParseDevice and SeAccessCheck. I have some old (outdated) NT source code I can (mostly) follow along by watching for specific calls, but my x86-Fu isn’t strong enough for me to grok what’s going on at source level most of the time.

There are two issues here: Why are you getting back access denied, and if this is related to the ACL what is the ACL set to?
I hate to ask the obvious, but have you looked at the ACL on the Device Object in question?

I manually examined the arguments going into SeAccessCheck for both (failure/success) cases. In my notes below, values are in hex and differences between cases are called out with “(Fail)” and “(Success)”:

IN PSECURITY_DESCRIPTOR SecurityDescriptor
    {
       UCHAR Revision .. 0x01
       UCHAR Sbz1 ...... 0x00
       SECURITY_DESCRIPTOR_CONTROL Control
            (Fail) ..... 0x9814 (SE_SELF_RELATIVE | SE_DACL_PROTECTED | SE_SACL_AUTO_INHERITED | SE_SACL_PRESENT | SE_DACL_PRESENT)
            (Success) .. 0x8014 (SE_SELF_RELATIVE |                                              SE_SACL_PRESENT | SE_DACL_PRESENT)
       PSID Owner
       {
           BYTE  Revision ................................ 01
           BYTE  SubAuthorityCount ....................... 02
           SID_IDENTIFIER_AUTHORITY IdentifierAuthority .. 00 00 00 00 00 05 [SECURITY_NT_AUTHORITY]
           DWORD SubAuthority[ANYSIZE_ARRAY]
           ................................................[0] 00000020 [NETWORK_SERVICE]
           ................................................[1] 00000220 [BUILTIN_ADMINISTRATORS]
       }
       PSID Group
       {
           BYTE  Revision ................................ 01
           BYTE  SubAuthorityCount ....................... 01
           SID_IDENTIFIER_AUTHORITY IdentifierAuthority .. 00 00 00 00 00 05 [SECURITY_NT_AUTHORITY]
           DWORD SubAuthority[ANYSIZE_ARRAY]
           ................................................[0] 00000012 [RESTRICTED_CODE]
       }
       PACL Sacl
       {
            BYTE AclRevision .. 02
            BYTE Sbz1 ......... 00
            WORD AclSize ...... 001c
            WORD AceCount ..... 0001
            WORD Sbz2 ......... 0000
            .................... 11 00 14 00 01 00 00 00 01 01 00 00 00 00 00 10 00 10 00 00
       }
       PACL Dacl
       {
            BYTE AclRevision .. 02
            BYTE Sbz1 ......... 00
            WORD AclSize
                (Fail) ........ 001c
                (Success) ..... 006c
            WORD AceCount
                (Fail) ........ 0001
                (Success) ..... 0004
            WORD Sbz2 ......... 0000
                (Fail) ........ 00 00 14 00 ff 01 1f 00 01 01 00 00 00 00 00 05 12 00 00 00
                (Success) ..... 00 00 14 00 bf 01 12 00 01 01 00 00 00 00 00 01 00 00 00 00
       }
    } // PSECURITY_DESCRIPTOR SecurityDescriptor

IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
    PACCESS_TOKEN ClientToken ........................ 00000000`00000000
    SECURITY_IMPERSONATION_LEVEL ImpersonationLevel .. 0x00000000 [SecurityAnonymous]
    PACCESS_TOKEN PrimaryToken ....................... (non-null in both cases, but didn't record this)
    PVOID ProcessAuditId
        (Fail) ....................................... 0x00000000000006d4 (???)
        (Success) .................................... 0x0000000000001d2c (???)
IN BOOLEAN SubjectContextLocked ......... TRUE
IN ACCESS_MASK DesiredAccess ............ 00 10 0080 (SYNCHRONIZE | FILE_READ_ATTRIBUTES)
IN ACCESS_MASK PreviouslyGrantedAccess .. 00 00 0000
OUT PPRIVILEGE_SET * Privileges ......... (non-null in both cases)
IN PGENERIC_MAPPING GenericMapping
    ACCESS_MASK GenericRead ..... 00 12 0089 (SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA)
    ACCESS_MASK GenericWrite .... 00 12 0116 (SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA)
    ACCESS_MASK GenericExecute .. 00 12 00a0 (SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_EXECUTE)
    ACCESS_MASK GenericAll ...... 00 1f 01ff (SYNCHRONIZE | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER | FILE_WRITE_ATTRIBUTES | ...all...)
IN KPROCESSOR_MODE AccessMode .......... 0x01 [UserMode]
OUT PACCESS_MASK GrantedAccess ......... (non-null in both cases)
OUT PNTSTATUS AccessStatus ............. (non-null in both cases)

I’m reading up on ACLs, access tokens, privileges, security descriptors and SIDs right now. I’ve never really understood Windows security very well.