]> Cypherpunks repositories - gostls13.git/commitdiff
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)
committerKeith Randall <khr@google.com>
Tue, 28 Mar 2023 19:55:43 +0000 (19:55 +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 #59293

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>

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 ad25b9ff32d29bf95bc9f90f2f7660c9b72e53ac..bdc40a8e7cc936f72e988db8be658dbcc80c23d3 100644 (file)
@@ -292,7 +292,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 e49ba5ee71645d555735a07efe02b4fb3311907a..a37604963fe96c42af134b072b862b081cfc74c6 100644 (file)
@@ -3199,7 +3199,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 07ddd0458ffcf9a5a1afed332d28b271ab415c6a..bfa0c5480f1d4c11f847857d7cd5276a940a683b 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 e20ffc2a619b2b5d1a360a5c052f9c3d05148e99..1d757a62a58a278384fa644b664e87ef496ab670 100644 (file)
@@ -193,6 +193,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")
+       }
+}