diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 5f2f600174..00c18104eb 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -1651,6 +1651,35 @@ func TestSelect(t *testing.T) { } } +func TestSelectMaxCases(t *testing.T) { + var sCases []SelectCase + channel := make(chan int) + close(channel) + for i := 0; i < 65536; i++ { + sCases = append(sCases, SelectCase{ + Dir: SelectRecv, + Chan: ValueOf(channel), + }) + } + // Should not panic + _, _, _ = Select(sCases) + sCases = append(sCases, SelectCase{ + Dir: SelectRecv, + Chan: ValueOf(channel), + }) + defer func() { + if err := recover(); err != nil { + if err.(string) != "reflect.Select: too many cases (max 65536)" { + t.Fatalf("unexpected error from select call with greater than max supported cases") + } + } else { + t.Fatalf("expected select call to panic with greater than max supported cases") + } + }() + // Should panic + _, _, _ = Select(sCases) +} + // selectWatch and the selectWatcher are a watchdog mechanism for running Select. // If the selectWatcher notices that the select has been blocked for >1 second, it prints // an error describing the select and panics the entire test binary. diff --git a/src/reflect/value.go b/src/reflect/value.go index 2b7dd66a8c..0f5e083663 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -2156,7 +2156,11 @@ type SelectCase struct { // and, if that case was a receive operation, the value received and a // boolean indicating whether the value corresponds to a send on the channel // (as opposed to a zero value received because the channel is closed). +// Select supports a maximum of 65536 cases. func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) { + if len(cases) > 65536 { + panic("reflect.Select: too many cases (max 65536)") + } // NOTE: Do not trust that caller is not modifying cases data underfoot. // The range is safe because the caller cannot modify our copy of the len // and each iteration makes its own copy of the value c. diff --git a/src/runtime/select.go b/src/runtime/select.go index 8033b6512f..a069e3e050 100644 --- a/src/runtime/select.go +++ b/src/runtime/select.go @@ -108,8 +108,9 @@ func block() { // selectgo implements the select statement. // // cas0 points to an array of type [ncases]scase, and order0 points to -// an array of type [2*ncases]uint16. Both reside on the goroutine's -// stack (regardless of any escaping in selectgo). +// an array of type [2*ncases]uint16 where ncases must be <= 65536. +// Both reside on the goroutine's stack (regardless of any escaping in +// selectgo). // // selectgo returns the index of the chosen scase, which matches the // ordinal position of its respective select{recv,send,default} call. @@ -120,6 +121,8 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) { print("select: cas0=", cas0, "\n") } + // NOTE: In order to maintain a lean stack size, the number of scases + // is capped at 65536. cas1 := (*[1 << 16]scase)(unsafe.Pointer(cas0)) order1 := (*[1 << 17]uint16)(unsafe.Pointer(order0))