syscall: clean up variable declarations in forkAndExecInChild

The various forkAndExecInChild implementations have comments
explaining that they pre-declare variables to force allocations
to occur before forking, but then later use ":=" declarations
for additional variables.

To make it clearer that those ":=" declarations do not allocate,
we move their declarations up to the predeclared blocks.

For #57208.

Change-Id: Ie8cb577fa7180b51b64d6dc398169053fdf8ea97
Reviewed-on: https://go-review.googlesource.com/c/go/+/456516
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/src/syscall/exec_bsd.go b/src/syscall/exec_bsd.go
index 32c3ebd..3798755 100644
--- a/src/syscall/exec_bsd.go
+++ b/src/syscall/exec_bsd.go
@@ -55,10 +55,13 @@
 	// Declare all variables at top in case any
 	// declarations require heap allocation (e.g., err1).
 	var (
-		r1     uintptr
-		err1   Errno
-		nextfd int
-		i      int
+		r1              uintptr
+		err1            Errno
+		nextfd          int
+		i               int
+		pgrp            _C_int
+		cred            *Credential
+		ngroups, groups uintptr
 	)
 
 	// guard against side effects of shuffling fds below.
@@ -119,7 +122,7 @@
 	if sys.Foreground {
 		// This should really be pid_t, however _C_int (aka int32) is
 		// generally equivalent.
-		pgrp := _C_int(sys.Pgid)
+		pgrp = _C_int(sys.Pgid)
 		if pgrp == 0 {
 			r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
 			if err1 != 0 {
@@ -149,9 +152,9 @@
 	}
 
 	// User and groups
-	if cred := sys.Credential; cred != nil {
-		ngroups := uintptr(len(cred.Groups))
-		groups := uintptr(0)
+	if cred = sys.Credential; cred != nil {
+		ngroups = uintptr(len(cred.Groups))
+		groups = uintptr(0)
 		if ngroups > 0 {
 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
 		}
diff --git a/src/syscall/exec_freebsd.go b/src/syscall/exec_freebsd.go
index af5a415..9e1cc46 100644
--- a/src/syscall/exec_freebsd.go
+++ b/src/syscall/exec_freebsd.go
@@ -60,10 +60,14 @@
 	// Declare all variables at top in case any
 	// declarations require heap allocation (e.g., err1).
 	var (
-		r1     uintptr
-		err1   Errno
-		nextfd int
-		i      int
+		r1              uintptr
+		err1            Errno
+		nextfd          int
+		i               int
+		pgrp            _C_int
+		cred            *Credential
+		ngroups, groups uintptr
+		upid            uintptr
 	)
 
 	// Record parent PID so child can test if it has died.
@@ -127,7 +131,7 @@
 	if sys.Foreground {
 		// This should really be pid_t, however _C_int (aka int32) is
 		// generally equivalent.
-		pgrp := _C_int(sys.Pgid)
+		pgrp = _C_int(sys.Pgid)
 		if pgrp == 0 {
 			r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
 			if err1 != 0 {
@@ -157,9 +161,9 @@
 	}
 
 	// User and groups
-	if cred := sys.Credential; cred != nil {
-		ngroups := uintptr(len(cred.Groups))
-		groups := uintptr(0)
+	if cred = sys.Credential; cred != nil {
+		ngroups = uintptr(len(cred.Groups))
+		groups = uintptr(0)
 		if ngroups > 0 {
 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
 		}
@@ -204,8 +208,8 @@
 		// using SIGKILL.
 		r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
 		if r1 != ppid {
-			pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
-			_, _, err1 = RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
+			upid, _, _ = RawSyscall(SYS_GETPID, 0, 0, 0)
+			_, _, err1 = RawSyscall(SYS_KILL, upid, uintptr(sys.Pdeathsig), 0)
 			if err1 != 0 {
 				goto childerror
 			}
diff --git a/src/syscall/exec_libc.go b/src/syscall/exec_libc.go
index ef0c87e..f8769b9 100644
--- a/src/syscall/exec_libc.go
+++ b/src/syscall/exec_libc.go
@@ -81,10 +81,13 @@
 	// Declare all variables at top in case any
 	// declarations require heap allocation (e.g., err1).
 	var (
-		r1     uintptr
-		err1   Errno
-		nextfd int
-		i      int
+		r1              uintptr
+		err1            Errno
+		nextfd          int
+		i               int
+		pgrp            _Pid_t
+		cred            *Credential
+		ngroups, groups uintptr
 	)
 
 	// guard against side effects of shuffling fds below.
@@ -135,7 +138,7 @@
 	}
 
 	if sys.Foreground {
-		pgrp := _Pid_t(sys.Pgid)
+		pgrp = _Pid_t(sys.Pgid)
 		if pgrp == 0 {
 			r1, err1 = getpid()
 			if err1 != 0 {
@@ -165,9 +168,9 @@
 	}
 
 	// User and groups
-	if cred := sys.Credential; cred != nil {
-		ngroups := uintptr(len(cred.Groups))
-		groups := uintptr(0)
+	if cred = sys.Credential; cred != nil {
+		ngroups = uintptr(len(cred.Groups))
+		groups = uintptr(0)
 		if ngroups > 0 {
 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
 		}
diff --git a/src/syscall/exec_libc2.go b/src/syscall/exec_libc2.go
index 41bc79a..9b04f96 100644
--- a/src/syscall/exec_libc2.go
+++ b/src/syscall/exec_libc2.go
@@ -52,14 +52,17 @@
 // functions that do not grow the stack.
 //
 //go:norace
-func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
+func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err1 Errno) {
 	// Declare all variables at top in case any
 	// declarations require heap allocation (e.g., err1).
 	var (
-		r1     uintptr
-		err1   Errno
-		nextfd int
-		i      int
+		r1              uintptr
+		nextfd          int
+		i               int
+		err             error
+		pgrp            _C_int
+		cred            *Credential
+		ngroups, groups uintptr
 	)
 
 	// guard against side effects of shuffling fds below.
@@ -94,7 +97,7 @@
 
 	// Enable tracing if requested.
 	if sys.Ptrace {
-		if err := ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
+		if err = ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
 			err1 = err.(Errno)
 			goto childerror
 		}
@@ -120,7 +123,7 @@
 	if sys.Foreground {
 		// This should really be pid_t, however _C_int (aka int32) is
 		// generally equivalent.
-		pgrp := _C_int(sys.Pgid)
+		pgrp = _C_int(sys.Pgid)
 		if pgrp == 0 {
 			r1, _, err1 = rawSyscall(abi.FuncPCABI0(libc_getpid_trampoline), 0, 0, 0)
 			if err1 != 0 {
@@ -149,9 +152,9 @@
 	}
 
 	// User and groups
-	if cred := sys.Credential; cred != nil {
-		ngroups := uintptr(len(cred.Groups))
-		groups := uintptr(0)
+	if cred = sys.Credential; cred != nil {
+		ngroups = uintptr(len(cred.Groups))
+		groups = uintptr(0)
 		if ngroups > 0 {
 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
 		}
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go
index 7e0c3d2..a8eb4bf 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -127,19 +127,19 @@
 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
 	// Set up and fork. This returns immediately in the parent or
 	// if there's an error.
-	r1, err1, p, locked := forkAndExecInChild1(argv0, argv, envv, chroot, dir, attr, sys, pipe)
+	upid, err, mapPipe, locked := forkAndExecInChild1(argv0, argv, envv, chroot, dir, attr, sys, pipe)
 	if locked {
 		runtime_AfterFork()
 	}
-	if err1 != 0 {
-		return 0, err1
+	if err != 0 {
+		return 0, err
 	}
 
 	// parent; return PID
-	pid = int(r1)
+	pid = int(upid)
 
 	if sys.UidMappings != nil || sys.GidMappings != nil {
-		Close(p[0])
+		Close(mapPipe[0])
 		var err2 Errno
 		// uid/gid mappings will be written after fork and unshare(2) for user
 		// namespaces.
@@ -148,8 +148,8 @@
 				err2 = err.(Errno)
 			}
 		}
-		RawSyscall(SYS_WRITE, uintptr(p[1]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
-		Close(p[1])
+		RawSyscall(SYS_WRITE, uintptr(mapPipe[1]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
+		Close(mapPipe[1])
 	}
 
 	return pid, 0
@@ -203,7 +203,7 @@
 //
 //go:noinline
 //go:norace
-func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (r1 uintptr, err1 Errno, p [2]int, locked bool) {
+func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid uintptr, err1 Errno, mapPipe [2]int, locked bool) {
 	// Defined in linux/prctl.h starting with Linux 4.3.
 	const (
 		PR_CAP_AMBIENT       = 0x2f
@@ -215,8 +215,15 @@
 	// processing in this stack frame and never returns, while the
 	// parent returns immediately from this frame and does all
 	// post-fork processing in the outer frame.
+	//
 	// Declare all variables at top in case any
-	// declarations require heap allocation (e.g., err1).
+	// declarations require heap allocation (e.g., err2).
+	// ":=" should not be used to declare any variable after
+	// the call to runtime_BeforeFork.
+	//
+	// NOTE(bcmills): The allocation behavior described in the above comment
+	// seems to lack a corresponding test, and it may be rendered invalid
+	// by an otherwise-correct change in the compiler.
 	var (
 		err2                      Errno
 		nextfd                    int
@@ -226,6 +233,11 @@
 		puid, psetgroups, pgid    []byte
 		uidmap, setgroups, gidmap []byte
 		clone3                    *cloneArgs
+		pgrp                      int32
+		dirfd                     int
+		cred                      *Credential
+		ngroups, groups           uintptr
+		c                         uintptr
 	)
 
 	if sys.UidMappings != nil {
@@ -264,7 +276,7 @@
 	// Allocate another pipe for parent to child communication for
 	// synchronizing writing of User ID/Group ID mappings.
 	if sys.UidMappings != nil || sys.GidMappings != nil {
-		if err := forkExecPipe(p[:]); err != nil {
+		if err := forkExecPipe(mapPipe[:]); err != nil {
 			err1 = err.(Errno)
 			return
 		}
@@ -288,17 +300,17 @@
 	runtime_BeforeFork()
 	locked = true
 	if clone3 != nil {
-		r1, err1 = rawVforkSyscall(_SYS_clone3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3))
+		pid, err1 = rawVforkSyscall(_SYS_clone3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3))
 	} else {
 		flags |= uintptr(SIGCHLD)
 		if runtime.GOARCH == "s390x" {
 			// On Linux/s390, the first two arguments of clone(2) are swapped.
-			r1, err1 = rawVforkSyscall(SYS_CLONE, 0, flags)
+			pid, err1 = rawVforkSyscall(SYS_CLONE, 0, flags)
 		} else {
-			r1, err1 = rawVforkSyscall(SYS_CLONE, flags, 0)
+			pid, err1 = rawVforkSyscall(SYS_CLONE, flags, 0)
 		}
 	}
-	if err1 != 0 || r1 != 0 {
+	if err1 != 0 || pid != 0 {
 		// If we're in the parent, we must return immediately
 		// so we're not in the same stack frame as the child.
 		// This can at most use the return PC, which the child
@@ -320,14 +332,14 @@
 
 	// Wait for User ID/Group ID mappings to be written.
 	if sys.UidMappings != nil || sys.GidMappings != nil {
-		if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(p[1]), 0, 0); err1 != 0 {
+		if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(mapPipe[1]), 0, 0); err1 != 0 {
 			goto childerror
 		}
-		r1, _, err1 = RawSyscall(SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
+		pid, _, err1 = RawSyscall(SYS_READ, uintptr(mapPipe[0]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
 		if err1 != 0 {
 			goto childerror
 		}
-		if r1 != unsafe.Sizeof(err2) {
+		if pid != unsafe.Sizeof(err2) {
 			err1 = EINVAL
 			goto childerror
 		}
@@ -355,11 +367,11 @@
 	}
 
 	if sys.Foreground {
-		pgrp := int32(sys.Pgid)
+		pgrp = int32(sys.Pgid)
 		if pgrp == 0 {
-			r1, _ = rawSyscallNoError(SYS_GETPID, 0, 0, 0)
+			pid, _ = rawSyscallNoError(SYS_GETPID, 0, 0, 0)
 
-			pgrp = int32(r1)
+			pgrp = int32(pid)
 		}
 
 		// Place process group in foreground.
@@ -381,11 +393,11 @@
 		}
 
 		if sys.Unshareflags&CLONE_NEWUSER != 0 && sys.GidMappings != nil {
-			dirfd := int(_AT_FDCWD)
+			dirfd = int(_AT_FDCWD)
 			if fd1, _, err1 = RawSyscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(&psetgroups[0])), uintptr(O_WRONLY), 0, 0, 0); err1 != 0 {
 				goto childerror
 			}
-			r1, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&setgroups[0])), uintptr(len(setgroups)))
+			pid, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&setgroups[0])), uintptr(len(setgroups)))
 			if err1 != 0 {
 				goto childerror
 			}
@@ -396,7 +408,7 @@
 			if fd1, _, err1 = RawSyscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(&pgid[0])), uintptr(O_WRONLY), 0, 0, 0); err1 != 0 {
 				goto childerror
 			}
-			r1, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&gidmap[0])), uintptr(len(gidmap)))
+			pid, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&gidmap[0])), uintptr(len(gidmap)))
 			if err1 != 0 {
 				goto childerror
 			}
@@ -406,11 +418,11 @@
 		}
 
 		if sys.Unshareflags&CLONE_NEWUSER != 0 && sys.UidMappings != nil {
-			dirfd := int(_AT_FDCWD)
+			dirfd = int(_AT_FDCWD)
 			if fd1, _, err1 = RawSyscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(&puid[0])), uintptr(O_WRONLY), 0, 0, 0); err1 != 0 {
 				goto childerror
 			}
-			r1, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&uidmap[0])), uintptr(len(uidmap)))
+			pid, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&uidmap[0])), uintptr(len(uidmap)))
 			if err1 != 0 {
 				goto childerror
 			}
@@ -443,9 +455,9 @@
 	}
 
 	// User and groups
-	if cred := sys.Credential; cred != nil {
-		ngroups := uintptr(len(cred.Groups))
-		groups := uintptr(0)
+	if cred = sys.Credential; cred != nil {
+		ngroups = uintptr(len(cred.Groups))
+		groups = uintptr(0)
 		if ngroups > 0 {
 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
 		}
@@ -474,7 +486,7 @@
 			goto childerror
 		}
 
-		for _, c := range sys.AmbientCaps {
+		for _, c = range sys.AmbientCaps {
 			// Add the c capability to the permitted and inheritable capability mask,
 			// otherwise we will not be able to add it to the ambient capability mask.
 			caps.data[capToIndex(c)].permitted |= capToMask(c)
@@ -485,7 +497,7 @@
 			goto childerror
 		}
 
-		for _, c := range sys.AmbientCaps {
+		for _, c = range sys.AmbientCaps {
 			_, _, err1 = RawSyscall6(SYS_PRCTL, PR_CAP_AMBIENT, uintptr(PR_CAP_AMBIENT_RAISE), c, 0, 0, 0)
 			if err1 != 0 {
 				goto childerror
@@ -511,9 +523,9 @@
 		// Signal self if parent is already dead. This might cause a
 		// duplicate signal in rare cases, but it won't matter when
 		// using SIGKILL.
-		r1, _ = rawSyscallNoError(SYS_GETPPID, 0, 0, 0)
-		if r1 != ppid {
-			pid, _ := rawSyscallNoError(SYS_GETPID, 0, 0, 0)
+		pid, _ = rawSyscallNoError(SYS_GETPPID, 0, 0, 0)
+		if pid != ppid {
+			pid, _ = rawSyscallNoError(SYS_GETPID, 0, 0, 0)
 			_, _, err1 = RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
 			if err1 != 0 {
 				goto childerror
diff --git a/src/syscall/exec_plan9.go b/src/syscall/exec_plan9.go
index 8f28b5a..8762237 100644
--- a/src/syscall/exec_plan9.go
+++ b/src/syscall/exec_plan9.go
@@ -135,6 +135,8 @@
 		errbuf   [ERRMAX]byte
 		statbuf  [STATMAX]byte
 		dupdevfd int
+		n        int
+		b        []byte
 	)
 
 	// Guard against side effects of shuffling fds below.
@@ -177,14 +179,14 @@
 dirloop:
 	for {
 		r1, _, _ = RawSyscall6(SYS_PREAD, uintptr(dupdevfd), uintptr(unsafe.Pointer(&statbuf[0])), uintptr(len(statbuf)), ^uintptr(0), ^uintptr(0), 0)
-		n := int(r1)
+		n = int(r1)
 		switch n {
 		case -1:
 			goto childerror
 		case 0:
 			break dirloop
 		}
-		for b := statbuf[:n]; len(b) > 0; {
+		for b = statbuf[:n]; len(b) > 0; {
 			var s []byte
 			s, b = gdirname(b)
 			if s == nil {