Commit 7b58581a authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: recognize (*[Big]T)(ptr)[:n:m] pattern for -d=checkptr

A common idiom for turning an unsafe.Pointer into a slice is to write:

    s := (*[Big]T)(ptr)[:n:m]

This technically violates Go's unsafe pointer rules (rule #1 says T2
can't be bigger than T1), but it's fairly common and not too difficult
to recognize, so might as well allow it for now so we can make
progress on #34972.

This should be revisited if #19367 is accepted.

Updates #22218.
Updates #34972.

Change-Id: Id824e2461904e770910b6e728b4234041d2cc8bc
Reviewed-on: https://go-review.googlesource.com/c/go/+/201839
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent dba19c65
...@@ -312,7 +312,7 @@ func runtimeTypes() []*types.Type { ...@@ -312,7 +312,7 @@ func runtimeTypes() []*types.Type {
typs[117] = functype(nil, []*Node{anonfield(typs[23]), anonfield(typs[23])}, []*Node{anonfield(typs[23])}) typs[117] = functype(nil, []*Node{anonfield(typs[23]), anonfield(typs[23])}, []*Node{anonfield(typs[23])})
typs[118] = functype(nil, []*Node{anonfield(typs[50])}, nil) typs[118] = functype(nil, []*Node{anonfield(typs[50])}, nil)
typs[119] = functype(nil, []*Node{anonfield(typs[50]), anonfield(typs[50])}, nil) typs[119] = functype(nil, []*Node{anonfield(typs[50]), anonfield(typs[50])}, nil)
typs[120] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[1])}, nil) typs[120] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[1]), anonfield(typs[50])}, nil)
typs[121] = types.NewSlice(typs[56]) typs[121] = types.NewSlice(typs[56])
typs[122] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[121])}, nil) typs[122] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[121])}, nil)
return typs[:] return typs[:]
......
...@@ -235,7 +235,7 @@ func racewriterange(addr, size uintptr) ...@@ -235,7 +235,7 @@ func racewriterange(addr, size uintptr)
func msanread(addr, size uintptr) func msanread(addr, size uintptr)
func msanwrite(addr, size uintptr) func msanwrite(addr, size uintptr)
func checkptrAlignment(unsafe.Pointer, *byte) func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer) func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
// architecture variants // architecture variants
......
...@@ -953,7 +953,7 @@ opswitch: ...@@ -953,7 +953,7 @@ opswitch:
n.Left = walkexpr(n.Left, init) n.Left = walkexpr(n.Left, init)
if n.Op == OCONVNOP && checkPtr(Curfn, 1) { if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T
n = walkCheckPtrAlignment(n, init) n = walkCheckPtrAlignment(n, init, nil)
break break
} }
if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
...@@ -1120,7 +1120,12 @@ opswitch: ...@@ -1120,7 +1120,12 @@ opswitch:
n.List.SetSecond(walkexpr(n.List.Second(), init)) n.List.SetSecond(walkexpr(n.List.Second(), init))
case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
n.Left = walkexpr(n.Left, init) checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.Etype == TUNSAFEPTR
if checkSlice {
n.Left.Left = walkexpr(n.Left.Left, init)
} else {
n.Left = walkexpr(n.Left, init)
}
low, high, max := n.SliceBounds() low, high, max := n.SliceBounds()
low = walkexpr(low, init) low = walkexpr(low, init)
if low != nil && isZero(low) { if low != nil && isZero(low) {
...@@ -1130,6 +1135,9 @@ opswitch: ...@@ -1130,6 +1135,9 @@ opswitch:
high = walkexpr(high, init) high = walkexpr(high, init)
max = walkexpr(max, init) max = walkexpr(max, init)
n.SetSliceBounds(low, high, max) n.SetSliceBounds(low, high, max)
if checkSlice {
n.Left = walkCheckPtrAlignment(n.Left, init, max)
}
if n.Op.IsSlice3() { if n.Op.IsSlice3() {
if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) { if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
// Reduce x[i:j:cap(x)] to x[i:j]. // Reduce x[i:j:cap(x)] to x[i:j].
...@@ -3912,13 +3920,29 @@ func isRuneCount(n *Node) bool { ...@@ -3912,13 +3920,29 @@ func isRuneCount(n *Node) bool {
return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES
} }
func walkCheckPtrAlignment(n *Node, init *Nodes) *Node { func walkCheckPtrAlignment(n *Node, init *Nodes, count *Node) *Node {
if n.Type.Elem().Alignment() == 1 && n.Type.Elem().Size() == 1 { if !n.Type.IsPtr() {
Fatalf("expected pointer type: %v", n.Type)
}
elem := n.Type.Elem()
if count != nil {
if !elem.IsArray() {
Fatalf("expected array type: %v", elem)
}
elem = elem.Elem()
}
size := elem.Size()
if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) {
return n return n
} }
if count == nil {
count = nodintconst(1)
}
n.Left = cheapexpr(n.Left, init) n.Left = cheapexpr(n.Left, init)
init.Append(mkcall("checkptrAlignment", nil, init, convnop(n.Left, types.Types[TUNSAFEPTR]), typename(n.Type.Elem()))) init.Append(mkcall("checkptrAlignment", nil, init, convnop(n.Left, types.Types[TUNSAFEPTR]), typename(elem), conv(count, types.Types[TUINTPTR])))
return n return n
} }
......
...@@ -9,18 +9,19 @@ import "unsafe" ...@@ -9,18 +9,19 @@ import "unsafe"
type ptrAlign struct { type ptrAlign struct {
ptr unsafe.Pointer ptr unsafe.Pointer
elem *_type elem *_type
n uintptr
} }
func checkptrAlignment(p unsafe.Pointer, elem *_type) { func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
// Check that (*T)(p) is appropriately aligned. // Check that (*[n]elem)(p) is appropriately aligned.
// TODO(mdempsky): What about fieldAlign? // TODO(mdempsky): What about fieldAlign?
if uintptr(p)&(uintptr(elem.align)-1) != 0 { if uintptr(p)&(uintptr(elem.align)-1) != 0 {
panic(ptrAlign{p, elem}) panic(ptrAlign{p, elem, n})
} }
// Check that (*T)(p) doesn't straddle multiple heap objects. // Check that (*[n]elem)(p) doesn't straddle multiple heap objects.
if elem.size != 1 && checkptrBase(p) != checkptrBase(add(p, elem.size-1)) { if size := n * elem.size; size > 1 && checkptrBase(p) != checkptrBase(add(p, size-1)) {
panic(ptrAlign{p, elem}) panic(ptrAlign{p, elem, n})
} }
} }
...@@ -34,6 +35,9 @@ func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) { ...@@ -34,6 +35,9 @@ func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
panic(ptrArith{p, originals}) panic(ptrArith{p, originals})
} }
// Check that if the computed pointer p points into a heap
// object, then one of the original pointers must have pointed
// into the same object.
base := checkptrBase(p) base := checkptrBase(p)
if base == 0 { if base == 0 {
return return
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment