unix: update the OpenBSD pledge interface to handle execpromises

The current Pledge function still takes into account the deprecated
"paths" argument, which has been replaced by "execpromises".
This change has been committed to OpenBSD on the 11 December 2017:
https://marc.info/?l=openbsd-tech&m=151302727506669.
This commit updates the Pledge function interface to reflect this
change.

Fixes golang/go#26824

Change-Id: Id6255b0432cf0a33e680e342dd23c7b02d0aa78f
Reviewed-on: https://go-review.googlesource.com/127762
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/unix/openbsd_pledge.go b/unix/openbsd_pledge.go
index 9b1e86a..11388e5 100644
--- a/unix/openbsd_pledge.go
+++ b/unix/openbsd_pledge.go
@@ -8,6 +8,9 @@
 package unix
 
 import (
+	"errors"
+	"fmt"
+	"strconv"
 	"syscall"
 	"unsafe"
 )
@@ -16,23 +19,77 @@
 	_SYS_PLEDGE = 108
 )
 
-// Pledge implements the pledge syscall. For more information see pledge(2).
-func Pledge(promises string, paths []string) error {
-	promisesPtr, err := syscall.BytePtrFromString(promises)
+// Pledge implements the pledge syscall.
+//
+// The pledge syscall does not accept execpromises on OpenBSD releases
+// before 6.3.
+//
+// execpromises must be empty when Pledge is called on OpenBSD
+// releases predating 6.3, otherwise an error will be returned.
+//
+// For more information see pledge(2).
+func Pledge(promises, execpromises string) error {
+	maj, min, err := majmin()
 	if err != nil {
 		return err
 	}
-	promisesUnsafe, pathsUnsafe := unsafe.Pointer(promisesPtr), unsafe.Pointer(nil)
-	if paths != nil {
-		var pathsPtr []*byte
-		if pathsPtr, err = syscall.SlicePtrFromStrings(paths); err != nil {
+
+	// If OpenBSD <= 5.9, pledge is not available.
+	if (maj == 5 && min != 9) || maj < 5 {
+		return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min)
+	}
+
+	// If OpenBSD <= 6.2 and execpromises is not empty
+	// return an error - execpromises is not available before 6.3
+	if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" {
+		return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min)
+	}
+
+	pptr, err := syscall.BytePtrFromString(promises)
+	if err != nil {
+		return err
+	}
+
+	// This variable will hold either a nil unsafe.Pointer or
+	// an unsafe.Pointer to a string (execpromises).
+	var expr unsafe.Pointer
+
+	// If we're running on OpenBSD > 6.2, pass execpromises to the syscall.
+	if maj > 6 || (maj == 6 && min > 2) {
+		exptr, err := syscall.BytePtrFromString(execpromises)
+		if err != nil {
 			return err
 		}
-		pathsUnsafe = unsafe.Pointer(&pathsPtr[0])
+		expr = unsafe.Pointer(exptr)
 	}
-	_, _, e := syscall.Syscall(_SYS_PLEDGE, uintptr(promisesUnsafe), uintptr(pathsUnsafe), 0)
+
+	_, _, e := syscall.Syscall(_SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
 	if e != 0 {
 		return e
 	}
+
 	return nil
 }
+
+// majmin returns major and minor version number for an OpenBSD system.
+func majmin() (major int, minor int, err error) {
+	var v Utsname
+	err = Uname(&v)
+	if err != nil {
+		return
+	}
+
+	major, err = strconv.Atoi(string(v.Release[0]))
+	if err != nil {
+		err = errors.New("cannot parse major version number returned by uname")
+		return
+	}
+
+	minor, err = strconv.Atoi(string(v.Release[2]))
+	if err != nil {
+		err = errors.New("cannot parse minor version number returned by uname")
+		return
+	}
+
+	return
+}
diff --git a/unix/openbsd_test.go b/unix/openbsd_test.go
index 734d765..3ded960 100644
--- a/unix/openbsd_test.go
+++ b/unix/openbsd_test.go
@@ -87,7 +87,7 @@
 func init() {
 	testProcs["pledge"] = testProc{
 		func() {
-			fmt.Println(unix.Pledge("", nil))
+			fmt.Println(unix.Pledge("", ""))
 			os.Exit(0)
 		},
 		func() error {