This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients Drekin, abarry, eryksun, tim.golden, troyhirni, vstinner
Date 2016-03-11.13:31:44
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1457703106.3.0.708755021231.issue18597@psf.upfronthosting.co.za>
In-reply-to
Content
Background Discussion

The Windows 10 console uses the condrv.sys device driver, which is set up as follows in the NT namespace:

    C:\>odir \ -r -n con;con*$;cond*
    Directory of \

    Device
        ConDrv <Device>
    Driver
        condrv <Driver>
    GLOBAL??
        CON -> \Device\ConDrv\Console
        CONIN$ -> \Device\ConDrv\CurrentIn
        CONOUT$ -> \Device\ConDrv\CurrentOut

Previously the base console API used an NT LPC port to communicate with the attached console process (i.e. an instance of conhost.exe). There wasn't a real console device. Instead, opening "CON", "CONIN$", or "CONOUT$" was special cased to call OpenConsoleW (undocumented). 

With the new console device driver, opening the DOS "CON" device gets translated to the NT path "\Device\ConDrv\Console", i.e. it opens the file named "Console" on the ConDrv device. 

Opening the Console file returns a handle for a regular kernel File object. To that end, you may have noticed that console handles in Windows 10 are no longer tagged for routing by setting the lower two bits (e.g. 3, 7, 11, etc). For example:

    >>> kernel32.GetStdHandle(STD_INPUT_HANDLE)
    32
    >>> kernel32.DebugBreak()
    (e1c.e20): Break instruction exception - code 80000003 (first chance)
    KERNELBASE!DebugBreak+0x2:
    00007ffa`60280262 cc              int     3

    0:000> !handle 32
    Handle 32
      Type          File

Previously, all operations on console handles were internally routed to special console functions, such as ReadFile => ReadConsoleA. Thus with the old LPC-based console, a ReadFile basically has the behavior of ReadConsoleA (with the addition of special casing input lines that start with Ctrl+Z). 

The new design scraps a lot of the special-cased code. For example, reading from a console handle in Windows 10 uses a regular NtReadFile system call. So the error it sets, if any at all, depends on translating the NTSTATUS code that's returned by NtReadFile. Let's see what status the console sets here:

    C:\Temp>cdb -xi ld python ccbug.py

    [...]

    ntdll!LdrpDoDebuggerBreak+0x30:
    00007ffb`170de260 cc              int     3
    0:000> g
    3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:54:25)
    [MSC v.1900 64 bit (AMD64)]

    calling DebugBreak...
    (8d0.62c): Break instruction exception - code 80000003 (first chance)
    KERNELBASE!DebugBreak+0x2:
    00007ffb`13f40262 cc              int     3
    0:000> bp ntdll!NtReadFile
    0:000> g
    Breakpoint 0 hit
    ntdll!NtReadFile:
    00007ffb`170b35d0 4c8bd1          mov     r10,rcx
    0:000> pt
    ntdll!NtReadFile+0xa:
    00007ffb`170b35da c3              ret
    0:000> r rax
    rax=0000000000000101

The console weirdly returns a success code, STATUS_ALERTED (0x101, "the delay completed because the thread was alerted"), which is why ReadFile doesn't set an error. STATUS_ALERTED is normally returned when an NT wait function gets alerted by NtAlertThread (note that this is not the same as getting alerted by an asynchronous procedure call). For example:
    
    tid = threading.get_ident()
    h = kernel32.OpenThread(MAXIMUM_ALLOWED, 0, tid)
    t = threading.Timer(5, ntdll.NtAlertThread, (h,))
    delay = LARGE_INTEGER(10 * -10**7) # 10 seconds
    t.start()
    r = ntdll.NtDelayExecution(True, byref(delay))

    >>> hex(r)
    '0x101'

NtAlertThread is rarely used because WinAPI wait functions (e.g. SleepEx) automatically restart a wait when the underlying NT wait returns STATUS_ALERTED. 

The ReadConsole implementation has always translated STATUS_ALERTED to ERROR_OPERATION_ABORTED. This still exists in the Windows 10 implementation of ReadConsole. However, the correct error status for this case is STATUS_CANCELLED (0xC0000120, "the I/O request was cancelled"): 

    >>> ntdll.RtlNtStatusToDosError(0xC0000120)
    995

Whoever reimplemented the console IPC using a device driver should have updated the console to return STATUS_CANCELLED when an I/O operation is interrupted by Ctrl+C or Ctrl+Break. Then nothing would need to be special cased.
History
Date User Action Args
2016-03-11 13:31:46eryksunsetrecipients: + eryksun, vstinner, tim.golden, Drekin, abarry, troyhirni
2016-03-11 13:31:46eryksunsetmessageid: <1457703106.3.0.708755021231.issue18597@psf.upfronthosting.co.za>
2016-03-11 13:31:46eryksunlinkissue18597 messages
2016-03-11 13:31:44eryksuncreate