From 9c78f7039debcbadf3968d902e144ac8953a5dd6 Mon Sep 17 00:00:00 2001 From: Jaden Weiss Date: Sun, 12 Apr 2020 19:08:44 -0400 Subject: [PATCH] runtime (chan): fix blocking select on a nil channel Previously, a blocking select on a nil channel would result in a nil panic inside the channel runtime code. This change fixes the nil checks so that the select works as intended. --- src/runtime/chan.go | 15 ++++++++++++--- testdata/channel.go | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 95243136..929b8c73 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -85,10 +85,12 @@ func (b *channelBlockedList) detach() { } for i, v := range b.allSelectOps { // cancel all other channel operations that are part of this select statement - if &b.allSelectOps[i] == b { + switch { + case &b.allSelectOps[i] == b: + // This entry is the one that was already detatched. continue - } - if v.s.ch == nil { + case v.t == nil: + // This entry is not used (nil channel). continue } v.s.ch.blocked = v.s.ch.blocked.remove(&b.allSelectOps[i]) @@ -512,6 +514,13 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB // construct blocked operations for i, v := range states { + if v.ch == nil { + // A nil channel receive will never complete. + // A nil channel send would have panicked during tryChanSelect. + ops[i] = channelBlockedList{} + continue + } + ops[i] = channelBlockedList{ next: v.ch.blocked, t: task.Current(), diff --git a/testdata/channel.go b/testdata/channel.go index db5a8631..63011d50 100644 --- a/testdata/channel.go +++ b/testdata/channel.go @@ -134,6 +134,8 @@ func main() { select { case make(chan int) <- 3: println("unreachable") + case <-(chan int)(nil): + println("unreachable") case n := <-ch: println("select n from chan:", n) case n := <-make(chan int):