syscall: use dup3 in forkAndExecInChild on NetBSD

Use dup3(oldfd, newfd, O_CLOEXEC) to atomically duplicate the file
descriptor and mark is as close-on-exec instead of dup2 & fcntl.

The dup3 syscall was added in NetBSD 6.0.

Change-Id: I01a4f8c62bfa8fb7f9f3166070380dd2002bb564
Reviewed-on: https://go-review.googlesource.com/c/go/+/358755
Trust: Tobias Klauser <tobias.klauser@gmail.com>
Trust: Benny Siegert <bsiegert@gmail.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/src/syscall/exec_bsd.go b/src/syscall/exec_bsd.go
index 4c36f9e..d2e50e3 100644
--- a/src/syscall/exec_bsd.go
+++ b/src/syscall/exec_bsd.go
@@ -8,6 +8,7 @@
 package syscall
 
 import (
+	"runtime"
 	"unsafe"
 )
 
@@ -181,11 +182,19 @@
 	// Pass 1: look for fd[i] < i and move those up above len(fd)
 	// so that pass 2 won't stomp on an fd it needs later.
 	if pipe < nextfd {
-		_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
-		if err1 != 0 {
-			goto childerror
+		switch runtime.GOOS {
+		case "netbsd":
+			_, _, err1 = RawSyscall(_SYS_DUP3, uintptr(pipe), uintptr(nextfd), O_CLOEXEC)
+			if err1 != 0 {
+				goto childerror
+			}
+		default:
+			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
+			if err1 != 0 {
+				goto childerror
+			}
+			RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
 		}
-		RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
 		pipe = nextfd
 		nextfd++
 	}
@@ -194,11 +203,19 @@
 			if nextfd == pipe { // don't stomp on pipe
 				nextfd++
 			}
-			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
-			if err1 != 0 {
-				goto childerror
+			switch runtime.GOOS {
+			case "netbsd":
+				_, _, err1 = RawSyscall(_SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
+				if err1 != 0 {
+					goto childerror
+				}
+			default:
+				_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
+				if err1 != 0 {
+					goto childerror
+				}
+				RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
 			}
-			RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
 			fd[i] = nextfd
 			nextfd++
 		}
diff --git a/src/syscall/syscall_dragonfly.go b/src/syscall/syscall_dragonfly.go
index 16adf30..cc92c4a 100644
--- a/src/syscall/syscall_dragonfly.go
+++ b/src/syscall/syscall_dragonfly.go
@@ -17,6 +17,8 @@
 	"unsafe"
 )
 
+const _SYS_DUP3 = 0
+
 // See version list in https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/param.h
 var (
 	osreldateOnce sync.Once
diff --git a/src/syscall/syscall_netbsd.go b/src/syscall/syscall_netbsd.go
index 6f05b0d..cebef10 100644
--- a/src/syscall/syscall_netbsd.go
+++ b/src/syscall/syscall_netbsd.go
@@ -14,6 +14,8 @@
 
 import "unsafe"
 
+const _SYS_DUP3 = SYS_DUP3
+
 type SockaddrDatalink struct {
 	Len    uint8
 	Family uint8
diff --git a/src/syscall/syscall_openbsd_mips64.go b/src/syscall/syscall_openbsd_mips64.go
index b259dc6..e8ae2e9 100644
--- a/src/syscall/syscall_openbsd_mips64.go
+++ b/src/syscall/syscall_openbsd_mips64.go
@@ -4,6 +4,8 @@
 
 package syscall
 
+const _SYS_DUP3 = 0
+
 func setTimespec(sec, nsec int64) Timespec {
 	return Timespec{Sec: sec, Nsec: nsec}
 }