Skip to content

Commit

Permalink
cmd/compile: recognize (*[Big]T)(ptr)[:n:m] pattern for -d=checkptr
Browse files Browse the repository at this point in the history
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 golang#34972.

This should be revisited if golang#19367 is accepted.

Updates golang#22218.
Updates golang#34972.

Change-Id: Id824e2461904e770910b6e728b4234041d2cc8bc
Reviewed-on: https://go-review.googlesource.com/c/go/+/201839
Run-TryBot: Matthew Dempsky <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Brad Fitzpatrick <[email protected]>
  • Loading branch information
mdempsky committed Oct 21, 2019
1 parent dba19c6 commit 7b58581
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/gc/builtin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/cmd/compile/internal/gc/builtin/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func racewriterange(addr, size uintptr)
func msanread(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)

// architecture variants
Expand Down
34 changes: 29 additions & 5 deletions src/cmd/compile/internal/gc/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ opswitch:
n.Left = walkexpr(n.Left, init)
if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T
n = walkCheckPtrAlignment(n, init)
n = walkCheckPtrAlignment(n, init, nil)
break
}
if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
Expand Down Expand Up @@ -1120,7 +1120,12 @@ opswitch:
n.List.SetSecond(walkexpr(n.List.Second(), init))

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 = walkexpr(low, init)
if low != nil && isZero(low) {
Expand All @@ -1130,6 +1135,9 @@ opswitch:
high = walkexpr(high, init)
max = walkexpr(max, init)
n.SetSliceBounds(low, high, max)
if checkSlice {
n.Left = walkCheckPtrAlignment(n.Left, init, max)
}
if n.Op.IsSlice3() {
if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
// Reduce x[i:j:cap(x)] to x[i:j].
Expand Down Expand Up @@ -3912,13 +3920,29 @@ func isRuneCount(n *Node) bool {
return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES
}

func walkCheckPtrAlignment(n *Node, init *Nodes) *Node {
if n.Type.Elem().Alignment() == 1 && n.Type.Elem().Size() == 1 {
func walkCheckPtrAlignment(n *Node, init *Nodes, count *Node) *Node {
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
}

if count == nil {
count = nodintconst(1)
}

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
}

Expand Down
16 changes: 10 additions & 6 deletions src/runtime/checkptr.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ import "unsafe"
type ptrAlign struct {
ptr unsafe.Pointer
elem *_type
n uintptr
}

func checkptrAlignment(p unsafe.Pointer, elem *_type) {
// Check that (*T)(p) is appropriately aligned.
func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
// Check that (*[n]elem)(p) is appropriately aligned.
// TODO(mdempsky): What about fieldAlign?
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.
if elem.size != 1 && checkptrBase(p) != checkptrBase(add(p, elem.size-1)) {
panic(ptrAlign{p, elem})
// Check that (*[n]elem)(p) doesn't straddle multiple heap objects.
if size := n * elem.size; size > 1 && checkptrBase(p) != checkptrBase(add(p, size-1)) {
panic(ptrAlign{p, elem, n})
}
}

Expand All @@ -34,6 +35,9 @@ func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
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)
if base == 0 {
return
Expand Down

0 comments on commit 7b58581

Please sign in to comment.