Another week, another way to locally crash the Windows kernel with an unhandled exception in ring-0 code (if you haven’t yet, see last week’s DoS in win32k!NtUserThunkedMenuItemInfo). Today, the bug is in the win32k!NtDCompositionBeginFrame system call handler, whose beginning can be translated into the following C-like pseudo-code:
NTSTATUS STDCALL NtDCompositionBeginFrame(HANDLE hDirComp, PINPUT_STRUCTURE lpInput, POUTPUT_STRUCTURE lpOutput) {
  NTSTATUS st;
  INPUT_STRUCTURE Input;
  DirectComposition::CConnection *Connection;
  if (lpInput != NULL) {
    try {
      ProbeForRead(lpInput, sizeof(INPUT_STRUCTURE), 1);
      RtlCopyMemory(&Input, lpInput, sizeof(INPUT_STRUCTURE));
      st = STATUS_SUCCCESS;
    } __except(EXCEPTION_EXECUTE_HANDLER) {
      st = GetExceptionCode();
    }
  } else {
    st = STATUS_INVALID_PARAMETER;
  }
  KeEnterCriticalRegion();
  if (NT_SUCCESS(st)) {
    st = DirectComposition::CConnection::ReferenceHandle(hDirComp, &Connection);
    if (NT_SUCCESS(st)) {
      if (Microsoft_Windows_Win32kEnableBits & 1) {
        Template_xq(&DCompBeginFrameEvent, hDirComp, lpInput->SomeField);
      }
      [...]
    }
  }
  [...]
}
Since the i/o structure names and definitions are not known to me, I just generically called them INPUT_STRUCTURE and OUTPUT_STRUCTURE; their details are non-essential to understand the bug. Here, we can see that the 2nd argument (lpInput) is accessed twice: once in line 9, with a proper sanitization with an inlined ProbeForRead call and a try/except block, but then also in line 23, where a field at offset 0x10 (SomeField in the above listing) is read from the user pointer while exception handling is disabled. The Template_xq function is just a thin wrapper around EtwWrite, which is used for logging kernel-mode events. This is the bug we want to exploit.