Issue 30237: Access violation due to CancelSynchronousIo of console read (original) (raw)

When ReadConsole is canceled by CancelSynchronousIo 1, for some reason the call still succeeds. It's probably related to the hack that maps STATUS_ALERTED to ERROR_OPERATION_ABORTED when a console read is interrupted by Ctrl+C.

The problem is that, when canceled, ReadConsole doesn't update the value of lpNumberOfCharsRead. Thus in read_console_w in Modules/_io/winconsoleio.c, the value of n is a random number that gets added to readlen, which is subsequently used to index into buf. The problem is the same for n_read in _PyOS_WindowsConsoleReadline.

For example, in 3.6:

import sys, ctypes, threading
kernel32 = ctypes.WinDLL('kernel32')
hMain = kernel32.OpenThread(1, 0, kernel32.GetCurrentThreadId())
t = threading.Timer(30, kernel32.CancelSynchronousIo, (hMain,))
t.start()
sys.stdin.readline()


Breakpoint 0 hit
KERNELBASE!ReadConsoleW:
00007ffc`fb558200 4053            push    rbx
0:000> pt
KERNELBASE!GetImmersiveColorTypeFromName+0x49d2:
00007ffc`fb5d2672 c3              ret
0:000> r rax
rax=0000000000000001
0:000> ?? @$teb->LastErrorValue == 995
bool true

0:000> gu
python36!read_console_w+0x8b:
00000000`6e43a483 85c0            test    eax,eax
0:000> ?? n
unsigned long 0xefdc39e8
0:000> g
(1154.11fc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
python36!read_console_w+0x11f:
00000000`6e43a517 66833a0a        cmp     word ptr [rdx],0Ah
                                        ds:000001e8`cfb72c7e=????

If the value of n is initialized to (DWORD)-1, then checking for a failed or canceled call could be implemented as follows:

if (!res || (n == (DWORD)-1 && GetLastError() ==
    ERROR_OPERATION_ABORTED)) {
    err = GetLastError();
    break;
}