[Bug 48986] Riot Vanguard (Riot Games) 'vgk.sys' crashes on unimplemented function ntoskrnl.exe.KeAreAllApcsDisabled

WineHQ Bugzilla wine-bugs at winehq.org
Wed May 13 11:04:50 CDT 2020


https://bugs.winehq.org/show_bug.cgi?id=48986

--- Comment #1 from Anastasius Focht <focht at gmx.net> ---
Hello folks,

some tidbits on why this API is called. Currently Wine doesn't implement the
concept of Windows kernel IRQL (priority level) by design.

IRQL is a pretty important topic for all code that executes in kernel space.
Kernel drivers need to follow general rules when/what code can be called,
depending on current IRQL, usage of synchronization primitives etc. I'm not
going repeat common knowledge here since you can read a lot about this topic in
various online resources.

Windows x86_64 stores the current IRQL in control register CR8. A read of CR8
register equals to 'KeGetCurrentIrql()'.

With Vanguard's 'vgk.sys' you'll see a lot of this:

--- snip ---
...
00c8:trace:seh:raise_exception code=c0000096 flags=0 addr=0x11233c1 ip=11233c1
tid=00c8
00c8:trace:seh:raise_exception  rax=000000005fd75f32 rbx=00000000000fccc8
rcx=0000000000000000 rdx=000000000000004a
00c8:trace:seh:raise_exception  rsi=0000000000cef8dc rdi=00000000000fccc8
rbp=0000000000cef660 rsp=0000000000cef560
00c8:trace:seh:raise_exception   r8=0000000000e2e320  r9=0000000000ceee92
r10=0000000000000000 r11=0000000000cef7b0
00c8:trace:seh:raise_exception  r12=00000000000fcb60 r13=00007fffffea4000
r14=0000000000000004 r15=0000000000000000
00c8:trace:seh:call_vectored_handlers calling handler at 0x22ce30 code=c0000096
flags=0
00c8:trace:int:emulate_instruction mov cr8,rax at 11233c1
00c8:trace:int:vectored_handler next instruction rip=11233c5
00c8:trace:int:vectored_handler   rax=0000000000000000 rbx=00000000000fccc8
rcx=0000000000000000 rdx=000000000000004a
00c8:trace:int:vectored_handler   rsi=0000000000cef8dc rdi=00000000000fccc8
rbp=0000000000cef660 rsp=0000000000cef560
00c8:trace:int:vectored_handler    r8=0000000000e2e320  r9=0000000000ceee92
r10=0000000000000000 r11=0000000000cef7b0
00c8:trace:int:vectored_handler   r12=00000000000fcb60 r13=00000000ffea4000
r14=0000000000000004 r15=0000000000000000
00c8:trace:seh:call_vectored_handlers handler at 0x22ce30 returned ffffffff
... 
--- snip ---

Disassembly from exception site (with obfuscation in between):

--- snip ---
...
00000000011233C1 | mov rax,cr8                             | KeGetCurrentIrql
00000000011233C5 | clc                                     |
00000000011233C6 | cmp edx,ebx                             |
00000000011233C8 | sub r15d,r15d                           |
00000000011233CB | cmp cl,EE                               |
00000000011233CE | test al,al                              | >= APC_LEVEL?
00000000011233D0 | jne vgk.E1F250                          |
00000000011233D6 | mov rax,FFFFF78000000014                | PASSIVE_LEVEL
00000000011233E0 | lea rdx,qword ptr ss:[rsp+68]           |
00000000011233E5 | not cx                                  |
00000000011233E8 | movsxd rcx,r8d                          |
00000000011233EB | lea rcx,qword ptr ss:[rsp+60]           |
00000000011233F0 | jmp vgk.11233F5                         |
00000000011233F5 | mov rax,qword ptr ds:[rax]              |
00000000011233F8 | mov qword ptr ss:[rsp+60],rax           |
00000000011233FD | call qword ptr ds:[<&RtlSystemTime...>] |
...
0000000000E1F250 | mov rcx,qword ptr ss:[rbp+130]          |
0000000000E1F257 | xor rcx,rsp                             |
0000000000E1F25A | call vgk.E2D670                         |
0000000000E1F25F | add rsp,248                             |
0000000000E1F266 | pop r15                                 |
0000000000E1F268 | pop r14                                 |
0000000000E1F26A | pop r13                                 |
0000000000E1F26C | pop r12                                 |
0000000000E1F26E | pop rdi                                 |
0000000000E1F26F | pop rsi                                 |
0000000000E1F270 | pop rbx                                 |
0000000000E1F271 | pop rbp                                 |
0000000000E1F272 | ret                                     |
...
--- snip --- 

It's used in the logging sequence, when the driver wants to write encrypted
data to log file. I've found a kernel driver on github that shows how it's
intended to be used in this case:

https://github.com/veracrypt/VeraCrypt/blob/94d3a1919c8eee4d7bfd7280ee4964477dee02e3/src/Driver/Ntdriver.c#L3596

--- snip ---
NTSTATUS SendDeviceIoControlRequest (PDEVICE_OBJECT deviceObject, ULONG
ioControlCode, void *inputBuffer, int inputBufferSize, void *outputBuffer, int
outputBufferSize)
{
    IO_STATUS_BLOCK ioStatusBlock;
    NTSTATUS status;
    PIRP irp;
    KEVENT event;

    if ((KeGetCurrentIrql() >= APC_LEVEL) || VC_KeAreAllApcsDisabled())
    {
        SendDeviceIoControlRequestWorkItemArgs args;

        PIO_WORKITEM workItem = IoAllocateWorkItem (RootDeviceObject);
        if (!workItem)
            return STATUS_INSUFFICIENT_RESOURCES;

        args.deviceObject = deviceObject;
        args.ioControlCode = ioControlCode;
        args.inputBuffer = inputBuffer;
        args.inputBufferSize = inputBufferSize;
        args.outputBuffer = outputBuffer;
        args.outputBufferSize = outputBufferSize;

        KeInitializeEvent (&args.WorkItemCompletedEvent, SynchronizationEvent,
FALSE);
        IoQueueWorkItem (workItem, SendDeviceIoControlRequestWorkItemRoutine,
DelayedWorkQueue, &args);

        KeWaitForSingleObject (&args.WorkItemCompletedEvent, Executive,
KernelMode, FALSE, NULL);
        IoFreeWorkItem (workItem);

        return args.Status;
    }

    KeInitializeEvent (&event, NotificationEvent, FALSE);

    irp = IoBuildDeviceIoControlRequest (ioControlCode, deviceObject,
inputBuffer, inputBufferSize,
        outputBuffer, outputBufferSize, FALSE, &event, &ioStatusBlock);

    if (!irp)
        return STATUS_INSUFFICIENT_RESOURCES;

    ObReferenceObject (deviceObject);

    status = IoCallDriver (deviceObject, irp);
    if (status == STATUS_PENDING)
    {
        KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatusBlock.Status;
    }

    ObDereferenceObject (deviceObject);
    return status;
}
--- snip --- 

Wine currently returns hard-coded PASSIVE_LEVEL = KIRQL(0).

https://source.winehq.org/git/wine.git/blob/26b26a2e0efcb776e7b0115f15580d2507b10400:/dlls/ntoskrnl.exe/instr.c#l701

This is ok and the code seems to work as intended. There will be cases in
future where we probably need a tracking of "fake" KIRQL and sync with CR8
emulation.

If we would return APC_LEVEL = KIRQL(1) or even DISPATCH_LEVEL = KIRQL(2) in
CR8 emulation, the call to KeAreAllApcsDisabled() wouldn't be made and no
logging functions would be executed.

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list