Summary
protectTerminalState in pkg/login/interactive_login.go puts the terminal into raw mode for the login prompt and restores it from a goroutine when the user interrupts. The channel it listens on is unbuffered:
signalChan := make(chan os.Signal)
signal.Notify(signalChan, os.Interrupt)
signal.Notify never blocks — it does a non-blocking send and discards the signal if nobody is receiving. The goroutine starts immediately afterwards, but a Ctrl-C arriving in that window is dropped, the restore never runs, and the terminal is left in the state the prompt put it in: no echo, until the user types reset.
Why it was not caught
go vet detects this (sigchanyzer), but CI ran only go test -short ./pkg/.... go test runs a reduced vet suite — printf, bools, atomic and a handful more — and only over the packages it tests, and sigchanyzer is not in that set.
Fix
Buffer the channel by one, and run the full go vet ./... in CI so the class is caught rather than asserted once.
Note this was already fixed incidentally on release/v3.0.0 in f2de696 and never came back to main, so only the 2.x line carries it.
Summary
protectTerminalStateinpkg/login/interactive_login.goputs the terminal into raw mode for the login prompt and restores it from a goroutine when the user interrupts. The channel it listens on is unbuffered:signal.Notifynever blocks — it does a non-blocking send and discards the signal if nobody is receiving. The goroutine starts immediately afterwards, but a Ctrl-C arriving in that window is dropped, the restore never runs, and the terminal is left in the state the prompt put it in: no echo, until the user typesreset.Why it was not caught
go vetdetects this (sigchanyzer), but CI ran onlygo test -short ./pkg/....go testruns a reduced vet suite — printf, bools, atomic and a handful more — and only over the packages it tests, andsigchanyzeris not in that set.Fix
Buffer the channel by one, and run the full
go vet ./...in CI so the class is caught rather than asserted once.Note this was already fixed incidentally on
release/v3.0.0in f2de696 and never came back tomain, so only the 2.x line carries it.