sort.Sort(byStackVar(fn.Dcl))
// Reassign stack offsets of the locals that are used.
+ lastHasPtr := false
for i, n := range fn.Dcl {
if n.Op != ONAME || n.Class() != PAUTO {
continue
if w >= thearch.MAXWIDTH || w < 0 {
Fatalf("bad width")
}
+ if w == 0 && lastHasPtr {
+ // Pad between a pointer-containing object and a zero-sized object.
+ // This prevents a pointer to the zero-sized object from being interpreted
+ // as a pointer to the pointer-containing object (and causing it
+ // to be scanned when it shouldn't be). See issue 24993.
+ w = 1
+ }
s.stksize += w
s.stksize = Rnd(s.stksize, int64(n.Type.Align))
if types.Haspointers(n.Type) {
s.stkptrsize = s.stksize
+ lastHasPtr = true
+ } else {
+ lastHasPtr = false
}
if thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
s.stksize = Rnd(s.stksize, int64(Widthptr))
--- /dev/null
+// asmcheck
+
+// Copyright 2018 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.
+
+// Make sure a pointer variable and a zero-sized variable
+// aren't allocated to the same stack slot.
+// See issue 24993.
+
+package codegen
+
+func zeroSize() {
+ c := make(chan struct{})
+ // amd64:`MOVQ\t\$0, ""\.s\+32\(SP\)`
+ var s *int
+ g(&s) // force s to be a stack object
+
+ // amd64:`LEAQ\t""\..*\+31\(SP\)`
+ c <- struct{}{}
+}
+
+//go:noinline
+func g(p **int) {
+}