Possible causes of STATUS_DEVICE_POWER_FAILURE for a virtual display adapter?

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.

Hi Marcel,

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

I was fairly certain that I’d introduced this during refactoring and enhancement of the V1 driver to the V2 driver. I was typing up a response to your last post this morning when I realized that I’d never actually checked to confirm that this doesn’t happen with the V1 driver.

The V1 driver didn’t have a custom IOCTL interface, so I think I initially discarded the idea. But it’s a simple matter to add a single WdfDeviceCreateDeviceInterface API call (in the same place) to the V1 code, so I did that, built it and ran the console test against it…

The V1 driver behaves the same; it initially fails the CreateFile call, but succeeds after I disable/re-enable the device in Device Manager.

So, I’m still betting this is down to something I did wrong, but I don’t need to go through the tedious process of looking for some subtle difference between my V1 and V2 drivers. Whatever this is, it was present from the beginning. I only noticed recently because the V1 driver didn’t have a custom interface.

Your advice emphasizes my suggestion to try the security descriptor in the INF file

Agreed. I’m reading up on Windows security at the moment. I’ve dealt with it intermittently in the past, but always in a yak shaving context. This time I’m going to try to understand it thoroughly enough that I don’t forget it all before I need it again.

I’ll try your suggestion and report back as soon as I figure out what my security descriptor should be…

Thanks!

Oh, and I’m also going to build the Microsoft example that V1 was based upon, and test that too. I’ll let you know.

You are really coming down on this thing, aren’t you?
I like it!
And who knows - maybe we will even really find a bug in the WDK IddCx sample or framework?
Wouldn’t be the first one I found (last one was in the 2007 Wave Cyclic audio sample - confirmed and fixed by MS).
IddCx framework is still very young (introduced in 2016 with Win 10 1607).
So the chances aren’t all that bad :wink:

Marcel Ruedinger
datronicsoft

There’s are WinDbg extensions !sd and !acl, in case they’d help. The ACL isn’t useful unless you can actuially read it. :wink:

This is extremely strange. It’s GOT to be something very simple that’s crossing you up. Hmmmm… wish I could offer-up more useful suggestions. I assume you’ve got Driver Verifier and WDF Verifier all enabled. And I assume that you’ve checked the log. Geez… who’s changing the ACL on the device, when, and more interestingly WHY?

Peter

Mr. Viscarola,
before spending too much time on this, you might probably want to know why I supplied a security descriptor for the INF file. I wanted my INF based security descriptor (which doesn’t require admin privileges) to overrule the PDO security. Careful - this PDO is created using SwDeviceCreate(…) which can also specify a security descriptor. I wanted to avoid all these details and eliminate exactly these degrees of freedom which are confusing us now. Unfortunately, my suggestions haven’t been followed yet…

Marcel Ruedinger
datronicsoft

Oh, and I’m also going to build the Microsoft example that V1 was based upon, and test that too. I’ll let you know.

  1. Made sure I had the latest copy of the Windows driver samples

  2. Modified driver (Windows-driver-samples\video\IndirectDisplay\IddSampleDriver) in the same way as above:

…add a single WdfDeviceCreateDeviceInterface API call (in the same place)

  1. Installed on two different test machines, and ran my console tester.

Issue exists with the sample driver

CreateFilecall returns Access Denied on the initial test after install. After I cycle enabled on the device, the CreateFile call succeeds.

I haven’t tried specifying an SDDL in the .inf file yet – still reading… I’ll post here after I do