From: Matthew Dempsky Date: Thu, 17 Oct 2019 21:29:16 +0000 (-0700) Subject: cmd/compile: recognize (*[Big]T)(ptr)[:n:m] pattern for -d=checkptr X-Git-Tag: go1.14beta1~660 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=7b58581a232829f29a26d6ebee9f4c3ca59b4771;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index ab65696a09..17c45cab15 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -312,7 +312,7 @@ func runtimeTypes() []*types.Type { 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[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[122] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[121])}, nil) return typs[:] diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go index 10a2241597..3fc82c2681 100644 --- a/src/cmd/compile/internal/gc/builtin/runtime.go +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -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 diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 8f6da25471..78de8114d0 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -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 @@ -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) { @@ -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]. @@ -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 } diff --git a/src/runtime/checkptr.go b/src/runtime/checkptr.go index a6d33c5af1..d1fc651509 100644 --- a/src/runtime/checkptr.go +++ b/src/runtime/checkptr.go @@ -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}) } } @@ -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