Re: [PATCH] Cygwin: console: Fix regression in console input
Johannes Schindelin <[email protected]>
| Newsgroups | gmane.os.cygwin.patches |
|---|---|
| Message-ID | <[email protected]> |
Hi Takashi,
On Mon, 3 Aug 2026, Takashi Yano wrote:
> The commit fac73911f5a0 ("Cygwin: console: Fix typeahead input for
> bash") introduced a bug where select() consumes some input chars in
> canonical mode, preventing read() from reading them. This is due to
> discarding input events when process_input_message() does not return
> `input_ok` even if it is called from select().
> The basic idea of that commit was making process_input_message()
> not to store processed chars into readahead buffer. This was not
> correct because the key input events ware processed twice, once by
> select() and again by read(). Thus even if that commit worked as
> intended, the side effect such as input echo would be applied twice.
>
> With this patch, process_input_message() handles only the minimum
> necessary of input events in both cases, those processed by select()
> and those processed by read(). To achive this behaviour, the function
> returns without processing when `input_ready` is already satisfied,
> or after it has processed the specified number of chars.
>
> Addresses: https://cygwin.com/pipermail/cygwin/2026-August/259915.html
> Reported-by: Steven Doerfler <[email protected]>
> Fixes: fac73911f5a0 ("Cygwin: console: Fix typeahead input for bash")
> Signed-off-by: Takashi Yano <[email protected]>
> Revewed-by:
This explanation, as well as the diff, look sound to me.
You may want to fix the typos "Revewed" -> "Reviewed", "ware" -> "were"
and "achive" -> "achieve", but those are tiny nits.
You could also fix the pre-existing typo in the comment "recored" ->
"recorded" while at it, but again, that's just a nit.
> ---
> winsup/cygwin/fhandler/console.cc | 14 ++++++--------
> 1 file changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc
> index 0219a37ef..3904fbd57 100644
> --- a/winsup/cygwin/fhandler/console.cc
> +++ b/winsup/cygwin/fhandler/console.cc
> @@ -1406,7 +1406,6 @@ fhandler_console::input_states
> fhandler_console::process_input_message (size_t len)
> {
> char tmp[60];
> - size_t num_chars = 0;
>
> if (!shared_console_info[unit])
> return input_error;
> @@ -1429,6 +1428,10 @@ fhandler_console::process_input_message (size_t len)
> return input_error;
> }
>
> + /* len == 0 if called from select.cc:peek_console() */
> + if (input_ready && (len == 0 || (get_ttyp ()->ti.c_lflag & ICANON)))
> + return input_ok;
It is worth pointing out that while this `input_ready &&` part looks
superfluous (both callers guard the call behind `!input_ready`), it is a
concurrency guard: a second thread reading the same console fd can set
`inpuy_ready` in the window between `read()`'s lock-free `while
(!input_ready)` check and `acquire_input_mutex`.
Maybe worth an extra code comment?
> +
> for (i = 0; i < total_read; i ++)
> {
> DWORD nread = 1;
> @@ -1794,7 +1797,6 @@ fhandler_console::process_input_message (size_t len)
> }
>
> num_input_events_processed = i + 1;
> - num_chars += nread;
> if (toadd)
> {
> ssize_t ret;
> @@ -1813,21 +1815,17 @@ fhandler_console::process_input_message (size_t len)
> }
> }
> /* len == 0 if called from select.cc:peek_console() */
> - if (len && num_chars >= len)
> + if (input_ready && (len == 0 || con_ra.ralen >= len))
Something GPT 5.6 Sol stumbled over, before noticing that this is a
pre-existing issue: the raw multi-pass caller passes full `buflen` (not
`buflen - copied_chars`). That was already there before this patch (and
both GPT and Opus think that this is incorrect, I haven't had time to wrap
my head around it), and the new `ralen`-based check is actually
neutral-to-better; and `tcflush(TCIFLUSH)` doesn't flush `con_ra`.
I'm fairly certain that this is out of scope for this here bug fix, if it
is an issue at all (what do you think? Is this `buflen` issue real? Should
`tcflush()` also flush `con_ra`?).
In any case, I'm happy with this patch! Feel free to add my "Reviewed-by".
Thanks!
Johannes
> goto out;
> }
> out:
> - if (len == 0)
> - /* If len == 0, cancel reading from console input buffer.
> - Clear readahead buffer. */
> - eat_readahead (-1);
> /* Discard processed recored. */
> DWORD discard_len = min (total_read, i + 1);
> /* If input is signalled, do not discard input here because
> discard_key_events() is already called from line_edit(). */
> if (stat == input_signalled)
> discard_len = 0;
> - if (discard_len && (len || stat != input_ok))
> + if (discard_len)
> discard_key_events (discard_len);
> return stat;
> }
> --
> 2.51.0
>
>