fix panic: send on closed channel

on windows: if the term is resized when ReadString is being called and no one is reading form ws, we get a panic: send on closed channel.
It could make sense to poll getconsolescreenbufferinfo calls instead of this:
something started from the sigwinch() func

on unix: same story, sigwinch was listened upon by default but if the
signals (chan sends) were not handled, this could cause a crash

fix #7434
This commit is contained in:
Adrien Delorme 2019-04-18 13:20:41 +02:00
parent fa7b922e7a
commit 71c00c200f
7 changed files with 84 additions and 35 deletions

2
go.mod
View File

@ -68,7 +68,7 @@ require (
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939 github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
github.com/mattn/go-tty v0.0.0-20190407112021-83fae09cc007 github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859
github.com/miekg/dns v1.1.1 // indirect github.com/miekg/dns v1.1.1 // indirect
github.com/mitchellh/cli v1.0.0 github.com/mitchellh/cli v1.0.0
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7 github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7

4
go.sum
View File

@ -231,8 +231,8 @@ github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-tty v0.0.0-20190407112021-83fae09cc007 h1:xjZxmVDmDZoEsl2gV0qD0pyBH+wXmJIZd27wsNFphJk= github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 h1:smQbSzmT3EHl4EUwtFwFGmGIpiYgIiiPeVv1uguIQEE=
github.com/mattn/go-tty v0.0.0-20190407112021-83fae09cc007/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=

View File

@ -38,6 +38,10 @@ func (tty *TTY) Size() (int, int, error) {
return tty.size() return tty.size()
} }
func (tty *TTY) SizePixel() (int, int, int, int, error) {
return tty.sizePixel()
}
func (tty *TTY) Input() *os.File { func (tty *TTY) Input() *os.File {
return tty.input() return tty.input()
} }
@ -115,6 +119,6 @@ type WINSIZE struct {
H int H int
} }
func (tty *TTY) SIGWINCH() chan WINSIZE { func (tty *TTY) SIGWINCH() <-chan WINSIZE {
return tty.sigwinch() return tty.sigwinch()
} }

View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"os" "os"
"syscall" "syscall"
"errors"
) )
type TTY struct { type TTY struct {
@ -54,6 +55,11 @@ func (tty *TTY) size() (int, int, error) {
return 80, 24, nil return 80, 24, nil
} }
func (tty *TTY) sizePixel() (int, int, int, int, error) {
x, y, _ := tty.size()
return x, y, -1, -1, errors.New("no implemented method for querying size in pixels on Plan 9")
}
func (tty *TTY) input() *os.File { func (tty *TTY) input() *os.File {
return tty.in return tty.in
} }

View File

@ -18,7 +18,6 @@ type TTY struct {
bin *bufio.Reader bin *bufio.Reader
out *os.File out *os.File
termios syscall.Termios termios syscall.Termios
ws chan WINSIZE
ss chan os.Signal ss chan os.Signal
} }
@ -48,24 +47,8 @@ func open() (*TTY, error) {
return nil, err return nil, err
} }
tty.ws = make(chan WINSIZE)
tty.ss = make(chan os.Signal, 1) tty.ss = make(chan os.Signal, 1)
signal.Notify(tty.ss, syscall.SIGWINCH)
go func() {
defer close(tty.ws)
for sig := range tty.ss {
switch sig {
case syscall.SIGWINCH:
if w, h, err := tty.size(); err == nil {
tty.ws <- WINSIZE{
W: w,
H: h,
}
}
default:
}
}
}()
return tty, nil return tty, nil
} }
@ -86,11 +69,16 @@ func (tty *TTY) close() error {
} }
func (tty *TTY) size() (int, int, error) { func (tty *TTY) size() (int, int, error) {
x, y, _, _, err := tty.sizePixel()
return x, y, err
}
func (tty *TTY) sizePixel() (int, int, int, int, error) {
var dim [4]uint16 var dim [4]uint16
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 { if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 {
return -1, -1, err return -1, -1, -1, -1, err
} }
return int(dim[1]), int(dim[0]), nil return int(dim[1]), int(dim[0]), int(dim[2]), int(dim[3]), nil
} }
func (tty *TTY) input() *os.File { func (tty *TTY) input() *os.File {
@ -127,6 +115,28 @@ func (tty *TTY) raw() (func() error, error) {
}, nil }, nil
} }
func (tty *TTY) sigwinch() chan WINSIZE { func (tty *TTY) sigwinch() <-chan WINSIZE {
return tty.ws signal.Notify(tty.ss, syscall.SIGWINCH)
ws := make(chan WINSIZE)
go func() {
defer close(ws)
for sig := range tty.ss {
if sig != syscall.SIGWINCH {
continue
}
w, h, err := tty.size()
if err != nil {
continue
}
// send but do not block for it
select {
case ws <- WINSIZE{W: w, H: h}:
default:
}
}
}()
return ws
} }

View File

@ -3,7 +3,9 @@
package tty package tty
import ( import (
"context"
"os" "os"
"errors"
"syscall" "syscall"
"unsafe" "unsafe"
@ -122,11 +124,13 @@ type charInfo struct {
} }
type TTY struct { type TTY struct {
in *os.File in *os.File
out *os.File out *os.File
st uint32 st uint32
rs []rune rs []rune
ws chan WINSIZE ws chan WINSIZE
sigwinchCtx context.Context
sigwinchCtxCancel context.CancelFunc
} }
func readConsoleInput(fd uintptr, record *inputRecord) (err error) { func readConsoleInput(fd uintptr, record *inputRecord) (err error) {
@ -183,6 +187,7 @@ func open() (*TTY, error) {
procSetConsoleMode.Call(h, uintptr(st)) procSetConsoleMode.Call(h, uintptr(st))
tty.ws = make(chan WINSIZE) tty.ws = make(chan WINSIZE)
tty.sigwinchCtx, tty.sigwinchCtxCancel = context.WithCancel(context.Background())
return tty, nil return tty, nil
} }
@ -206,10 +211,24 @@ func (tty *TTY) readRune() (rune, error) {
switch ir.eventType { switch ir.eventType {
case windowBufferSizeEvent: case windowBufferSizeEvent:
wr := (*windowBufferSizeRecord)(unsafe.Pointer(&ir.event)) wr := (*windowBufferSizeRecord)(unsafe.Pointer(&ir.event))
tty.ws <- WINSIZE{ ws := WINSIZE{
W: int(wr.size.x), W: int(wr.size.x),
H: int(wr.size.y), H: int(wr.size.y),
} }
if err := tty.sigwinchCtx.Err(); err != nil {
// closing
// the following select might panic without this guard close
return 0, err
}
select {
case tty.ws <- ws:
case <-tty.sigwinchCtx.Done():
return 0, tty.sigwinchCtx.Err()
default:
return 0, nil // no one is currently trying to read
}
case keyEvent: case keyEvent:
kr := (*keyEventRecord)(unsafe.Pointer(&ir.event)) kr := (*keyEventRecord)(unsafe.Pointer(&ir.event))
if kr.keyDown != 0 { if kr.keyDown != 0 {
@ -307,8 +326,9 @@ func (tty *TTY) readRune() (rune, error) {
} }
func (tty *TTY) close() error { func (tty *TTY) close() error {
close(tty.ws)
procSetConsoleMode.Call(tty.in.Fd(), uintptr(tty.st)) procSetConsoleMode.Call(tty.in.Fd(), uintptr(tty.st))
tty.sigwinchCtxCancel()
close(tty.ws)
return nil return nil
} }
@ -321,6 +341,15 @@ func (tty *TTY) size() (int, int, error) {
return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil
} }
func (tty *TTY) sizePixel() (int, int, int, int, error) {
x, y, err := tty.size()
if err != nil {
x = -1
y = -1
}
return x, y, -1, -1, errors.New("no implemented method for querying size in pixels on Windows")
}
func (tty *TTY) input() *os.File { func (tty *TTY) input() *os.File {
return tty.in return tty.in
} }
@ -349,6 +378,6 @@ func (tty *TTY) raw() (func() error, error) {
}, nil }, nil
} }
func (tty *TTY) sigwinch() chan WINSIZE { func (tty *TTY) sigwinch() <-chan WINSIZE {
return tty.ws return tty.ws
} }

2
vendor/modules.txt vendored
View File

@ -299,7 +299,7 @@ github.com/mattn/go-colorable
github.com/mattn/go-isatty github.com/mattn/go-isatty
# github.com/mattn/go-runewidth v0.0.4 # github.com/mattn/go-runewidth v0.0.4
github.com/mattn/go-runewidth github.com/mattn/go-runewidth
# github.com/mattn/go-tty v0.0.0-20190407112021-83fae09cc007 # github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859
github.com/mattn/go-tty github.com/mattn/go-tty
# github.com/mitchellh/cli v1.0.0 # github.com/mitchellh/cli v1.0.0
github.com/mitchellh/cli github.com/mitchellh/cli