Skip to content

Commit 3317d7f

Browse files
committed
unix: use libc stubs for OpenBSD pledge+unveil
For both compatibility and security concerns, it is preferrable to perform system calls through the libc stubs rather than syscall(2). Besides making programs resilient to changing system call numbers, it removes an unnecessary use of the syscall(2) call which, if found by an attacker, could be abused to perform any system call. As OpenBSD 6.2 is the oldest supported version of OpenBSD, remove the handling of pledge on versions prior to this release. PledgeExecpromises is no longer usable on 6.2, as the execpromises is treated as required (unlike Pledge, where on 6.2 it is allowed but must be empty). The empty execpromises string is no longer converted to a nil pointer on 6.2. This fixes an out-of-bounds read where, on 6.2, an empty string would be passed to the deprecated pledge(2) API, which would interpret the pointer as an array of strings.
1 parent 1bfbee0 commit 3317d7f

7 files changed

+144
-76
lines changed

unix/pledge_openbsd.go

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
"errors"
99
"fmt"
1010
"strconv"
11-
"syscall"
12-
"unsafe"
1311
)
1412

1513
// Pledge implements the pledge syscall.
@@ -20,42 +18,48 @@ import (
2018
// execpromises must be empty when Pledge is called on OpenBSD
2119
// releases predating 6.3, otherwise an error will be returned.
2220
//
21+
// On OpenBSD 6.3 and later, an empty execpromises removes all
22+
// execpromises from the process. Use PledgePromises to only set
23+
// promises without setting execpromises.
24+
//
2325
// For more information see pledge(2).
2426
func Pledge(promises, execpromises string) error {
2527
maj, min, err := majmin()
2628
if err != nil {
2729
return err
2830
}
2931

30-
err = pledgeAvailable(maj, min, execpromises)
31-
if err != nil {
32-
return err
32+
// OS support for execpromises is required only when execpromises is not
33+
// the empty string.
34+
if execpromises != "" {
35+
err = supportsExecpromises(maj, min)
36+
if err != nil {
37+
return err
38+
}
3339
}
3440

35-
pptr, err := syscall.BytePtrFromString(promises)
41+
promisesBytes, err := BytePtrFromString(promises)
3642
if err != nil {
3743
return err
3844
}
3945

40-
// This variable will hold either a nil unsafe.Pointer or
41-
// an unsafe.Pointer to a string (execpromises).
42-
var expr unsafe.Pointer
46+
// This variable will hold either a nil pointer or a pointer to the
47+
// NUL-terminated execpromises string.
48+
var execpromisesBytes *byte
4349

44-
// If we're running on OpenBSD > 6.2, pass execpromises to the syscall.
45-
if maj > 6 || (maj == 6 && min > 2) {
46-
exptr, err := syscall.BytePtrFromString(execpromises)
50+
// If we're running on OpenBSD >= 6.3, pass execpromises to the syscall.
51+
// While an empty execpromises string is required by this API on
52+
// OpenBSD <= 6.2 and has no effect, the empty execpromises string
53+
// removes all execpromises on OpenBSD >= 6.3.
54+
if maj > 6 || (maj == 6 && min >= 3) {
55+
exptr, err := BytePtrFromString(execpromises)
4756
if err != nil {
4857
return err
4958
}
50-
expr = unsafe.Pointer(exptr)
51-
}
52-
53-
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
54-
if e != 0 {
55-
return e
59+
execpromisesBytes = exptr
5660
}
5761

58-
return nil
62+
return pledge(promisesBytes, execpromisesBytes)
5963
}
6064

6165
// PledgePromises implements the pledge syscall.
@@ -64,62 +68,39 @@ func Pledge(promises, execpromises string) error {
6468
//
6569
// For more information see pledge(2).
6670
func PledgePromises(promises string) error {
67-
maj, min, err := majmin()
68-
if err != nil {
69-
return err
70-
}
71-
72-
err = pledgeAvailable(maj, min, "")
71+
promisesBytes, err := BytePtrFromString(promises)
7372
if err != nil {
7473
return err
7574
}
7675

77-
// This variable holds the execpromises and is always nil.
78-
var expr unsafe.Pointer
79-
80-
pptr, err := syscall.BytePtrFromString(promises)
81-
if err != nil {
82-
return err
83-
}
84-
85-
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
86-
if e != 0 {
87-
return e
88-
}
89-
90-
return nil
76+
return pledge(promisesBytes, nil)
9177
}
9278

9379
// PledgeExecpromises implements the pledge syscall.
9480
//
9581
// This changes the execpromises and leaves the promises untouched.
9682
//
83+
// The pledge syscall does not accept execpromises on OpenBSD releases
84+
// before 6.3.
85+
//
9786
// For more information see pledge(2).
9887
func PledgeExecpromises(execpromises string) error {
9988
maj, min, err := majmin()
10089
if err != nil {
10190
return err
10291
}
10392

104-
err = pledgeAvailable(maj, min, execpromises)
93+
err = supportsExecpromises(maj, min)
10594
if err != nil {
10695
return err
10796
}
10897

109-
// This variable holds the promises and is always nil.
110-
var pptr unsafe.Pointer
111-
112-
exptr, err := syscall.BytePtrFromString(execpromises)
98+
execpromisesBytes, err := BytePtrFromString(execpromises)
11399
if err != nil {
114100
return err
115101
}
116102

117-
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(pptr), uintptr(unsafe.Pointer(exptr)), 0)
118-
if e != 0 {
119-
return e
120-
}
121-
122-
return nil
103+
return pledge(nil, execpromisesBytes)
123104
}
124105

125106
// majmin returns major and minor version number for an OpenBSD system.
@@ -145,17 +126,11 @@ func majmin() (major int, minor int, err error) {
145126
return
146127
}
147128

148-
// pledgeAvailable checks for availability of the pledge(2) syscall
149-
// based on the running OpenBSD version.
150-
func pledgeAvailable(maj, min int, execpromises string) error {
151-
// If OpenBSD <= 5.9, pledge is not available.
152-
if (maj == 5 && min != 9) || maj < 5 {
153-
return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min)
154-
}
155-
156-
// If OpenBSD <= 6.2 and execpromises is not empty,
157-
// return an error - execpromises is not available before 6.3
158-
if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" {
129+
// supportsExecpromises checks for availability of the execpromises argument to
130+
// the pledge(2) syscall based on the running OpenBSD version.
131+
func supportsExecpromises(maj, min int) error {
132+
// execpromises is not available before 6.3
133+
if maj < 6 || (maj == 6 && min <= 2) {
159134
return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min)
160135
}
161136

unix/syscall_openbsd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,5 @@ func Uname(uname *Utsname) error {
327327
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
328328
//sys munmap(addr uintptr, length uintptr) (err error)
329329
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
330+
//sys pledge(promises *byte, execpromises *byte) (err error)
331+
//sys unveil(path *byte, flags *byte) (err error)

unix/unveil_openbsd.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,54 @@
44

55
package unix
66

7-
import (
8-
"syscall"
9-
"unsafe"
10-
)
7+
import "fmt"
118

129
// Unveil implements the unveil syscall.
1310
// For more information see unveil(2).
1411
// Note that the special case of blocking further
1512
// unveil calls is handled by UnveilBlock.
13+
//
14+
// Unveil requires OpenBSD 6.4 or later.
1615
func Unveil(path string, flags string) error {
17-
pathPtr, err := syscall.BytePtrFromString(path)
16+
err := supportsUnveil()
1817
if err != nil {
1918
return err
2019
}
21-
flagsPtr, err := syscall.BytePtrFromString(flags)
20+
pathBytes, err := BytePtrFromString(path)
2221
if err != nil {
2322
return err
2423
}
25-
_, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(unsafe.Pointer(pathPtr)), uintptr(unsafe.Pointer(flagsPtr)), 0)
26-
if e != 0 {
27-
return e
24+
flagsBytes, err := BytePtrFromString(flags)
25+
if err != nil {
26+
return err
2827
}
29-
return nil
28+
return unveil(pathBytes, flagsBytes)
3029
}
3130

3231
// UnveilBlock blocks future unveil calls.
3332
// For more information see unveil(2).
33+
//
34+
// Unveil requires OpenBSD 6.4 or later.
3435
func UnveilBlock() error {
35-
// Both pointers must be nil.
36-
var pathUnsafe, flagsUnsafe unsafe.Pointer
37-
_, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(pathUnsafe), uintptr(flagsUnsafe), 0)
38-
if e != 0 {
39-
return e
36+
err := supportsUnveil()
37+
if err != nil {
38+
return err
4039
}
40+
return unveil(nil, nil)
41+
}
42+
43+
// supportsUnveil checks for availability of the unveil(2) system call based
44+
// on the running OpenBSD version.
45+
func supportsUnveil() error {
46+
maj, min, err := majmin()
47+
if err != nil {
48+
return err
49+
}
50+
51+
// unveil is not available before 6.4
52+
if maj < 6 || (maj == 6 && min <= 3) {
53+
return fmt.Errorf("cannot use unveil on OpenBSD %d.%d", maj, min)
54+
}
55+
4156
return nil
4257
}

unix/zsyscall_openbsd_386.go

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

unix/zsyscall_openbsd_386.s

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,13 @@ TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
672672
JMP libc_utimensat(SB)
673673
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4
674674
DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB)
675+
676+
TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0
677+
JMP libc_pledge(SB)
678+
GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4
679+
DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB)
680+
681+
TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0
682+
JMP libc_unveil(SB)
683+
GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4
684+
DATA ·libc_unveil_trampoline_addr(SB)/4, $libc_unveil_trampoline<>(SB)

unix/zsyscall_openbsd_amd64.go

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

unix/zsyscall_openbsd_amd64.s

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,13 @@ TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
672672
JMP libc_utimensat(SB)
673673
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8
674674
DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB)
675+
676+
TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0
677+
JMP libc_pledge(SB)
678+
GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8
679+
DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB)
680+
681+
TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0
682+
JMP libc_unveil(SB)
683+
GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8
684+
DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB)

0 commit comments

Comments
 (0)