microsoft / microsoft/terminal

ReadConsole returns TRUE after CancelIoEx

Open
#17,791 0 comments 0 reactions 0 assignees View on GitHub
Area-Server Issue-Bug Priority-1 Product-Meta Tracking-External zInbox-Bug
Dominant language
C++
Stars
105k
Forks
9.6k
Avg merge
3d 17h
Merged PRs (30d)
29

Description

### Windows Terminal version

Latest source

### Windows build number

10.0.19045.4780

### Other Software

No

### Steps to reproduce

1. Compile the code:

Code

```C++
#ifndef UNICODE
#define UNICODE
#endif

#ifndef _UNICODE
#define _UNICODE
#endif

#include
#include
#include
#include

#include

using namespace std::literals;

int main()
{
const auto In = GetStdHandle(STD_INPUT_HANDLE);

std::thread Thread([&]
{
std::this_thread::sleep_for(3s);
CancelIoEx(In, {});
});

std::wcout << L"Do not touch anything for about 3s" << std::endl;

wchar_t Buffer[1024];
DWORD NumberOfCharsRead{};
const auto Result = ReadConsole(In, Buffer, ARRAYSIZE(Buffer), &NumberOfCharsRead, {});
const auto Error = GetLastError();

std::wcout << L"Result: " << Result << std::endl;
std::wcout << L"Error: " << Error << std::endl;
std::wcout << L"Chars: " << NumberOfCharsRead << std::endl;
std::wcout << L"Data: " << std::wstring(Buffer, NumberOfCharsRead) << std::endl;

Thread.join();

std::wcout << L"Now enter something: " << std::endl;

if (ReadConsole(In, Buffer, ARRAYSIZE(Buffer), &NumberOfCharsRead, {}))
{
std::wcout << L"You have entered: " << std::wstring(Buffer, NumberOfCharsRead) << std::endl;
}
}
```

2. Run it (anywhere: WT, OpenConsole, conhost)
3. Inspect the output

### Expected Behavior

The program reads the console input in a blocking way.
If there is no input, another thread issues CancelIoEx after 3 seconds.

Since the `ReadConsole` did not read anything, it should return FALSE:
- It is logical.
- Even Raymond Chen [says so](https://devblogs.microsoft.com/oldnewthing/20150323-00/?p=44413):
> If you had used Read­File instead of fgets, the read would have failed with error code ERROR_OPERATION_ABORTED, as documented by Cancel­Io­Ex.

So the program should print `Result: 0`.

### Actual Behavior

`ReadConsole` does not read anything, does not update `NumberOfCharsRead`, but returns **TRUE**.

It also leaves the input in somewhat inconsistent state, which you can see by typing something after the cancellation: the first input will be discarded. I think this was already mentioned here: https://github.com/microsoft/terminal/issues/12143#issuecomment-1895629003.

The incorrect return value is much worse though: it is a common pattern to leave `NumberOfCharsRead` uninitialized, because either `ReadConsole` succeeds and initializes it, or it fails and it makes no sense to look there anyway.
In the code above I initialized it, but if I didn't do so an uninitialized read would've occurred, from both `NumberOfCharsRead` and `Buffer`.

Notably the last error is correctly set to 995 - `ERROR_OPERATION_ABORTED` - "The I/O operation has been aborted because of either a thread exit or an application request.", but who checks the last error on successful calls?

Contributor guide

Open the contributing guide

Research direction

Start by running the supplied C++ reproducer in Windows Terminal, OpenConsole, or conhost, focusing on the ReadConsole call canceled by CancelIoEx. Trace the console input path and existing handling of ERROR_OPERATION_ABORTED. Done means a canceled read reports failure without consuming input, with behavior verified against the reproducer.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
cli, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.