diff options
author | Keith Randall <khr@golang.org> | 2024-05-08 08:51:39 -0700 |
---|---|---|
committer | Gopher Robot <gobot@golang.org> | 2024-05-15 17:21:37 +0000 |
commit | 3a842931184426ebb2d5f8605782991bca8b67b8 (patch) | |
tree | 9bd191facaf453a9235b93179d22b9f48b90c311 | |
parent | 362dcedfdb79804ce538e153cdcb6bb2a5b653ea (diff) | |
download | go-3a842931184426ebb2d5f8605782991bca8b67b8.tar.gz go-3a842931184426ebb2d5f8605782991bca8b67b8.zip |
[release-branch.go1.22] cmd/compile: avoid past-the-end pointer when zeroing
When we optimize append(s, make([]T, n)...), we have to be careful
not to pass &s[0] + len(s)*sizeof(T) as the argument to memclr, as that
pointer might be past-the-end. This can only happen if n is zero, so
just special-case n==0 in the generated code.
Fixes #67258
Change-Id: Ic680711bb8c38440eba5e759363ef65f5945658b
Reviewed-on: https://go-review.googlesource.com/c/go/+/584116
Reviewed-by: Austin Clements <austin@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@google.com>
(cherry picked from commit 93e3696b5dac778cf638a67616a4a4d521d6fce9)
Reviewed-on: https://go-review.googlesource.com/c/go/+/584315
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
-rw-r--r-- | src/cmd/compile/internal/walk/assign.go | 43 | ||||
-rw-r--r-- | test/fixedbugs/issue67255.go | 33 |
2 files changed, 59 insertions, 17 deletions
diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go index fc3b858a80..63b6a1d2c1 100644 --- a/src/cmd/compile/internal/walk/assign.go +++ b/src/cmd/compile/internal/walk/assign.go @@ -623,21 +623,23 @@ func isAppendOfMake(n ir.Node) bool { // panicmakeslicelen() // } // s := l1 -// n := len(s) + l2 -// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2. -// // cap is a positive int and n can become negative when len(s) + l2 -// // overflows int. Interpreting n when negative as uint makes it larger -// // than cap(s). growslice will check the int n arg and panic if n is -// // negative. This prevents the overflow from being undetected. -// if uint(n) <= uint(cap(s)) { -// s = s[:n] -// } else { -// s = growslice(T, s.ptr, n, s.cap, l2, T) +// if l2 != 0 { +// n := len(s) + l2 +// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2. +// // cap is a positive int and n can become negative when len(s) + l2 +// // overflows int. Interpreting n when negative as uint makes it larger +// // than cap(s). growslice will check the int n arg and panic if n is +// // negative. This prevents the overflow from being undetected. +// if uint(n) <= uint(cap(s)) { +// s = s[:n] +// } else { +// s = growslice(T, s.ptr, n, s.cap, l2, T) +// } +// // clear the new portion of the underlying array. +// hp := &s[len(s)-l2] +// hn := l2 * sizeof(T) +// memclr(hp, hn) // } -// // clear the new portion of the underlying array. -// hp := &s[len(s)-l2] -// hn := l2 * sizeof(T) -// memclr(hp, hn) // } // s // @@ -671,11 +673,18 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { s := typecheck.TempAt(base.Pos, ir.CurFunc, l1.Type()) nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, l1)) + // if l2 != 0 { + // Avoid work if we're not appending anything. But more importantly, + // avoid allowing hp to be a past-the-end pointer when clearing. See issue 67255. + nifnz := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.ONE, l2, ir.NewInt(base.Pos, 0)), nil, nil) + nifnz.Likely = true + nodes = append(nodes, nifnz) + elemtype := s.Type().Elem() // n := s.len + l2 nn := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT]) - nodes = append(nodes, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2))) + nifnz.Body = append(nifnz.Body, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2))) // if uint(n) <= uint(s.cap) nuint := typecheck.Conv(nn, types.Types[types.TUINT]) @@ -697,7 +706,7 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { l2)), } - nodes = append(nodes, nif) + nifnz.Body = append(nifnz.Body, nif) // hp := &s[s.len - l2] // TODO: &s[s.len] - hn? @@ -723,7 +732,7 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { // if growslice isn't called do we need to do the zeroing ourselves. nif.Body = append(nif.Body, clr...) } else { - nodes = append(nodes, clr...) + nifnz.Body = append(nifnz.Body, clr...) } typecheck.Stmts(nodes) diff --git a/test/fixedbugs/issue67255.go b/test/fixedbugs/issue67255.go new file mode 100644 index 0000000000..7ca7a239dd --- /dev/null +++ b/test/fixedbugs/issue67255.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +var zero int + +var sink any + +func main() { + var objs [][]*byte + for i := 10; i < 200; i++ { + // The objects we're allocating here are pointer-ful. Some will + // max out their size class, which are the ones we want. + // We also allocate from small to large, so that the object which + // maxes out its size class is the last one allocated in that class. + // This allocation pattern leaves the next object in the class + // unallocated, which we need to reproduce the bug. + objs = append(objs, make([]*byte, i)) + } + sink = objs // force heap allocation + + // Bug will happen as soon as the write barrier turns on. + for range 10000 { + sink = make([]*byte, 1024) + for _, s := range objs { + s = append(s, make([]*byte, zero)...) + } + } +} |