mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
reflect: update Select to panic early on excessive input cases
The runtime implementation of select has an upper limit on the number of select cases that are supported in order to maintain low stack memory usage. Rather than support an arbitrary number of select cases, we've opted to panic early with a useful message pointing the user directly at the problem. Fixes #37350 Change-Id: Id129ba281ae120387e681ef96be8adcf89725840 Reviewed-on: https://go-review.googlesource.com/c/go/+/220583 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
821b799482
commit
7802b55176
3 changed files with 38 additions and 2 deletions
|
|
@ -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.
|
// 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
|
// 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.
|
// an error describing the select and panics the entire test binary.
|
||||||
|
|
|
||||||
|
|
@ -2156,7 +2156,11 @@ type SelectCase struct {
|
||||||
// and, if that case was a receive operation, the value received and a
|
// 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
|
// boolean indicating whether the value corresponds to a send on the channel
|
||||||
// (as opposed to a zero value received because the channel is closed).
|
// (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) {
|
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.
|
// 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
|
// 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.
|
// and each iteration makes its own copy of the value c.
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,9 @@ func block() {
|
||||||
// selectgo implements the select statement.
|
// selectgo implements the select statement.
|
||||||
//
|
//
|
||||||
// cas0 points to an array of type [ncases]scase, and order0 points to
|
// 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
|
// an array of type [2*ncases]uint16 where ncases must be <= 65536.
|
||||||
// stack (regardless of any escaping in selectgo).
|
// Both reside on the goroutine's stack (regardless of any escaping in
|
||||||
|
// selectgo).
|
||||||
//
|
//
|
||||||
// selectgo returns the index of the chosen scase, which matches the
|
// selectgo returns the index of the chosen scase, which matches the
|
||||||
// ordinal position of its respective select{recv,send,default} call.
|
// 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")
|
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))
|
cas1 := (*[1 << 16]scase)(unsafe.Pointer(cas0))
|
||||||
order1 := (*[1 << 17]uint16)(unsafe.Pointer(order0))
|
order1 := (*[1 << 17]uint16)(unsafe.Pointer(order0))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue