recursive bool // recursive function or group of mutually recursive functions.
}
+// funcSym returns n.Nname.Sym if no nils are encountered along the way.
+func funcSym(n *Node) *Sym {
+ if n == nil || n.Nname == nil {
+ return nil
+ }
+ return n.Nname.Sym
+}
+
+// curfnSym returns n.Curfn.Nname.Sym if no nils are encountered along the way.
+func curfnSym(n *Node) *Sym {
+ return funcSym(n.Curfn)
+}
+
func escAnalyze(all *NodeList, recursive bool) {
var es EscState
e := &es
if Debug['m'] != 0 {
for l := e.noesc; l != nil; l = l.Next {
if l.N.Esc == EscNone {
- var tmp *Sym
- if l.N.Curfn != nil && l.N.Curfn.Nname != nil {
- tmp = l.N.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- Warnl(int(l.N.Lineno), "%v %v does not escape", tmp, Nconv(l.N, obj.FmtShort))
+ Warnl(int(l.N.Lineno), "%v %v does not escape", curfnSym(l.N), Nconv(l.N, obj.FmtShort))
}
}
}
}
if Debug['m'] > 1 {
- var tmp *Sym
- if Curfn != nil && Curfn.Nname != nil {
- tmp = Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, tmp, n)
+ fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn), n)
}
switch n.Op {
// Everything but fixed array is a dereference.
case ORANGE:
- if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil {
- escassign(e, n.List.Next.N, n.Right)
+ if n.List != nil && n.List.Next != nil {
+ if Isfixedarray(n.Type) {
+ escassign(e, n.List.Next.N, n.Right)
+ } else {
+ escassign(e, n.List.Next.N, addDereference(n.Right))
+ }
}
case OSWITCH:
// b escapes as well. If we ignore such OSLICEARR, we will conclude
// that b does not escape when b contents do.
if Debug['m'] != 0 {
- var tmp *Sym
- if n.Curfn != nil && n.Curfn.Nname != nil {
- tmp = n.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", tmp, Nconv(n.Left, obj.FmtShort))
+ Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", curfnSym(n), Nconv(n.Left, obj.FmtShort))
}
break
for ll := n.List.Next; ll != nil; ll = ll.Next {
escassign(e, &e.theSink, ll.N) // lose track of assign to dereference
}
+ } else {
+ // append(slice1, slice2...) -- slice2 itself does not escape, but contents do.
+ slice2 := n.List.Next.N
+ escassign(e, &e.theSink, addDereference(slice2)) // lose track of assign of dereference
+ if Debug['m'] > 2 {
+ Warnl(int(n.Lineno), "%v special treatment of append(slice1, slice2...) %v", curfnSym(n), Nconv(n, obj.FmtShort))
+ }
}
+ escassign(e, &e.theSink, addDereference(n.List.N)) // The original elements are now leaked, too
case OCONV, OCONVNOP:
escassign(e, n, n.Left)
case OARRAYLIT:
if Isslice(n.Type) {
- n.Esc = EscNone // until proven otherwise
+ // Slice itself is not leaked until proven otherwise
+ n.Esc = EscNone
e.noesc = list(e.noesc, n)
n.Escloopdepth = e.loopdepth
+ }
- // Values make it to memory, lose track.
- for ll := n.List; ll != nil; ll = ll.Next {
- escassign(e, &e.theSink, ll.N.Right)
- }
- } else {
- // Link values to array.
- for ll := n.List; ll != nil; ll = ll.Next {
- escassign(e, n, ll.N.Right)
- }
+ // Link values to array/slice
+ for ll := n.List; ll != nil; ll = ll.Next {
+ escassign(e, n, ll.N.Right)
}
// Link values to struct.
}
if Debug['m'] > 1 {
- var tmp *Sym
- if Curfn != nil && Curfn.Nname != nil {
- tmp = Curfn.Nname.Sym
- } else {
- tmp = nil
- }
fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n",
- Ctxt.Line(int(lineno)), e.loopdepth, tmp,
+ Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn),
Nconv(dst, obj.FmtShort), Jconv(dst, obj.FmtShort), Oconv(int(dst.Op), 0),
Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), Oconv(int(src.Op), 0))
}
case OAPPEND:
// Append returns first argument.
+ // Subsequent arguments are already leaked because they are operands to append.
escassign(e, dst, src.List.N)
case OINDEX:
// Index of array preserves input value.
if Isfixedarray(src.Left.Type) {
escassign(e, dst, src.Left)
+ } else {
+ escflows(e, dst, src)
}
// Might be pointer arithmetic, in which case
}
if Debug['m'] > 1 {
- var tmp *Sym
- if dst.Curfn != nil && dst.Curfn.Nname != nil {
- tmp = dst.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
- fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), tmp, dst.Escloopdepth)
+ fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), curfnSym(dst), dst.Escloopdepth)
}
for l := dst.Escflowsrc; l != nil; l = l.Next {
src.Esclevel = level
if Debug['m'] > 1 {
- var tmp *Sym
- if src.Curfn != nil && src.Curfn.Nname != nil {
- tmp = src.Curfn.Nname.Sym
- } else {
- tmp = nil
- }
fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d]\n",
- level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), tmp, src.Escloopdepth)
+ level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), curfnSym(src), src.Escloopdepth)
}
e.pdepth++
if Isfixedarray(src.Type) {
break
}
+ for ll := src.List; ll != nil; ll = ll.Next {
+ escwalk(e, level.dec(), dst, ll.N.Right)
+ }
+
fallthrough
case ODDDARG,
package escape
+import (
+ "os"
+ "strings"
+)
+
var sink interface{}
func slice0() {
}
func slice8() {
- // BAD: i should not escape here
- i := 0 // ERROR "moved to heap: i"
- s := []*int{&i} // ERROR "&i escapes to heap" "literal does not escape"
+ i := 0
+ s := []*int{&i} // ERROR "&i does not escape" "literal does not escape"
_ = s
}
s := []*int{&i} // ERROR "&i escapes to heap" "literal escapes to heap"
return s
}
+
+func envForDir(dir string) []string { // ERROR "dir does not escape"
+ env := os.Environ()
+ return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape"
+}
+
+func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r2 level=0"
+NextVar:
+ for _, inkv := range in {
+ k := strings.SplitAfterN(inkv, "=", 2)[0]
+ for i, outkv := range out {
+ if strings.HasPrefix(outkv, k) {
+ out[i] = inkv
+ continue NextVar
+ }
+ }
+ out = append(out, inkv)
+ }
+ return out
+}
+
+const (
+ IPv4len = 4
+ IPv6len = 16
+)
+
+var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
+
+func IPv4(a, b, c, d byte) IP {
+ p := make(IP, IPv6len) // ERROR "make\(IP, IPv6len\) escapes to heap"
+ copy(p, v4InV6Prefix)
+ p[12] = a
+ p[13] = b
+ p[14] = c
+ p[15] = d
+ return p
+}
+
+type IP []byte
+
+type IPAddr struct {
+ IP IP
+ Zone string // IPv6 scoped addressing zone
+}
+
+type resolveIPAddrTest struct {
+ network string
+ litAddrOrName string
+ addr *IPAddr
+ err error
+}
+
+var resolveIPAddrTests = []resolveIPAddrTest{
+ {"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+}
+
+func setupTestData() {
+ resolveIPAddrTests = append(resolveIPAddrTests,
+ []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest literal does not escape"
+ {"ip",
+ "localhost",
+ &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+ nil},
+ {"ip4",
+ "localhost",
+ &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+ nil},
+ }...)
+}