]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.20] cmd/compile: don't assume pointer of a slice is non-nil
authorKeith Randall <khr@golang.org>
Tue, 28 Mar 2023 17:19:21 +0000 (10:19 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Wed, 29 Mar 2023 19:07:23 +0000 (19:07 +0000)
unsafe.SliceData can return pointers which are nil. That function gets
lowered to the SSA OpSlicePtr, which the compiler assumes is non-nil.
This used to be the case as OpSlicePtr was only used in situations
where the bounds check already passed. But with unsafe.SliceData that
is no longer the case.

There are situations where we know it is nil. Use Bounded() to
indicate that.

I looked through all the uses of OSPTR and added SetBounded where it
made sense. Most OSPTR results are passed directly to runtime calls
(e.g. memmove), so even if we know they are non-nil that info isn't
helpful.

Fixes #59296

Change-Id: I437a15330db48e0082acfb1f89caf8c56723fc51
Reviewed-on: https://go-review.googlesource.com/c/go/+/479896
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Keith Randall <khr@golang.org>
(cherry picked from commit b899641ecea7d07c997282e985beb295c31d1097)
Reviewed-on: https://go-review.googlesource.com/c/go/+/479899
Run-TryBot: Keith Randall <khr@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
src/cmd/compile/internal/ir/node.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/walk/convert.go
src/cmd/compile/internal/walk/range.go
test/fixedbugs/issue59293.go [new file with mode: 0644]

index b42f914aadd8209c163c13a46441caa2ed1b09e1..547dd48a1474462835107b26cb3703bc1fc709b6 100644 (file)
@@ -291,7 +291,7 @@ const (
        OEFACE         // itable and data words of an empty-interface value.
        OITAB          // itable word of an interface value.
        OIDATA         // data word of an interface value in X
-       OSPTR          // base pointer of a slice or string.
+       OSPTR          // base pointer of a slice or string. Bounded==1 means known non-nil.
        OCFUNC         // reference to c function pointer (not go func value)
        OCHECKNIL      // emit code to ensure pointer/interface not nil
        ORESULT        // result of a function call; Xoffset is stack offset
index 52f94030df7e20abe3021b8ccc5e9c97115217ea..526332294c58099ea9b5ded441c5044ebc9d76c3 100644 (file)
@@ -3203,7 +3203,10 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
                n := n.(*ir.UnaryExpr)
                a := s.expr(n.X)
                if n.X.Type().IsSlice() {
-                       return s.newValue1(ssa.OpSlicePtr, n.Type(), a)
+                       if n.Bounded() {
+                               return s.newValue1(ssa.OpSlicePtr, n.Type(), a)
+                       }
+                       return s.newValue1(ssa.OpSlicePtrUnchecked, n.Type(), a)
                } else {
                        return s.newValue1(ssa.OpStringPtr, n.Type(), a)
                }
index bf06ed6f4679b7c54edde99b91f588681588e23e..ca0c806a1e1a5264530dd8cb7d1788e8f36ba57f 100644 (file)
@@ -281,7 +281,9 @@ func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
 
                // Copy from the static string data to the [n]byte.
                if len(sc) > 0 {
-                       as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), t.PtrTo())))
+                       sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
+                       sptr.SetBounded(true)
+                       as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(sptr, t.PtrTo())))
                        appendWalkStmt(init, as)
                }
 
index 64af26bf29c587c97d9241f0a2644d7853701e56..82e7d22cfa839701d22f22712ab1a87a00bc1e8b 100644 (file)
@@ -191,6 +191,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node {
                // Pointer to current iteration position. Start on entry to the loop
                // with the pointer in hu.
                ptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, hs)
+               ptr.SetBounded(true)
                huVal := ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], ptr)
                huVal = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUINTPTR], huVal)
                hu := typecheck.Temp(types.Types[types.TUINTPTR])
diff --git a/test/fixedbugs/issue59293.go b/test/fixedbugs/issue59293.go
new file mode 100644 (file)
index 0000000..1f05fe9
--- /dev/null
@@ -0,0 +1,28 @@
+// run
+
+// Copyright 2023 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
+
+import "unsafe"
+
+//go:noinline
+func f(x []byte) bool {
+       return unsafe.SliceData(x) != nil
+}
+
+//go:noinline
+func g(x string) bool {
+       return unsafe.StringData(x) != nil
+}
+
+func main() {
+       if f(nil) {
+               panic("bad f")
+       }
+       if g("") {
+               panic("bad g")
+       }
+}