Skip to content

Commit c8da038

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 4fee21c commit c8da038

7 files changed

+113
-76
lines changed

unix/pledge_openbsd.go

Lines changed: 31 additions & 57 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,46 @@ 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)
32+
// OS support for execpromises is required only when execpromises is not
33+
// the empty string.
34+
err = supportsExecpromises(maj, min, execpromises != "")
3135
if err != nil {
3236
return err
3337
}
3438

35-
pptr, err := syscall.BytePtrFromString(promises)
39+
_promises, err := BytePtrFromString(promises)
3640
if err != nil {
3741
return err
3842
}
3943

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

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)
48+
// If we're running on OpenBSD >= 6.3, pass execpromises to the syscall.
49+
// While an empty execpromises string is required by this API on
50+
// OpenBSD <= 6.2 and has no effect, the empty execpromises string
51+
// removes all execpromises on OpenBSD >= 6.3.
52+
if maj > 6 || (maj == 6 && min >= 3) {
53+
exptr, err := BytePtrFromString(execpromises)
4754
if err != nil {
4855
return err
4956
}
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
57+
_execpromises = exptr
5658
}
5759

58-
return nil
60+
return pledge(_promises, _execpromises)
5961
}
6062

6163
// PledgePromises implements the pledge syscall.
@@ -64,62 +66,39 @@ func Pledge(promises, execpromises string) error {
6466
//
6567
// For more information see pledge(2).
6668
func PledgePromises(promises string) error {
67-
maj, min, err := majmin()
68-
if err != nil {
69-
return err
70-
}
71-
72-
err = pledgeAvailable(maj, min, "")
73-
if err != nil {
74-
return err
75-
}
76-
77-
// This variable holds the execpromises and is always nil.
78-
var expr unsafe.Pointer
79-
80-
pptr, err := syscall.BytePtrFromString(promises)
69+
_promises, err := BytePtrFromString(promises)
8170
if err != nil {
8271
return err
8372
}
8473

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
74+
return pledge(_promises, nil)
9175
}
9276

9377
// PledgeExecpromises implements the pledge syscall.
9478
//
9579
// This changes the execpromises and leaves the promises untouched.
9680
//
81+
// The pledge syscall does not accept execpromises on OpenBSD releases
82+
// before 6.3.
83+
//
9784
// For more information see pledge(2).
9885
func PledgeExecpromises(execpromises string) error {
9986
maj, min, err := majmin()
10087
if err != nil {
10188
return err
10289
}
10390

104-
err = pledgeAvailable(maj, min, execpromises)
91+
err = supportsExecpromises(maj, min, true)
10592
if err != nil {
10693
return err
10794
}
10895

109-
// This variable holds the promises and is always nil.
110-
var pptr unsafe.Pointer
111-
112-
exptr, err := syscall.BytePtrFromString(execpromises)
96+
_execpromises, err := BytePtrFromString(execpromises)
11397
if err != nil {
11498
return err
11599
}
116100

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
101+
return pledge(nil, _execpromises)
123102
}
124103

125104
// majmin returns major and minor version number for an OpenBSD system.
@@ -145,17 +124,12 @@ func majmin() (major int, minor int, err error) {
145124
return
146125
}
147126

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-
127+
// supportsExecpromises checks for availability of the execpromises argument to
128+
// the pledge(2) syscall based on the running OpenBSD version.
129+
func supportsExecpromises(maj, min int, required bool) error {
156130
// If OpenBSD <= 6.2 and execpromises is not empty,
157131
// return an error - execpromises is not available before 6.3
158-
if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" {
132+
if (maj < 6 || (maj == 6 && min <= 2)) && required {
159133
return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min)
160134
}
161135

unix/syscall_openbsd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ func Uname(uname *Utsname) error {
314314
//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
315315
//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
316316
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
317+
//sys pledge(promises *byte, execpromises *byte) (err error)
318+
//sys unveil(path *byte, flags *byte) (err error)
317319

318320
/*
319321
* Unimplemented

unix/unveil_openbsd.go

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,24 @@
44

55
package unix
66

7-
import (
8-
"syscall"
9-
"unsafe"
10-
)
11-
127
// Unveil implements the unveil syscall.
138
// For more information see unveil(2).
149
// Note that the special case of blocking further
1510
// unveil calls is handled by UnveilBlock.
1611
func Unveil(path string, flags string) error {
17-
pathPtr, err := syscall.BytePtrFromString(path)
12+
_path, err := BytePtrFromString(path)
1813
if err != nil {
1914
return err
2015
}
21-
flagsPtr, err := syscall.BytePtrFromString(flags)
16+
_flags, err := BytePtrFromString(flags)
2217
if err != nil {
2318
return err
2419
}
25-
_, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(unsafe.Pointer(pathPtr)), uintptr(unsafe.Pointer(flagsPtr)), 0)
26-
if e != 0 {
27-
return e
28-
}
29-
return nil
20+
return unveil(_path, _flags)
3021
}
3122

3223
// UnveilBlock blocks future unveil calls.
3324
// For more information see unveil(2).
3425
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
40-
}
41-
return nil
26+
return unveil(nil, nil)
4227
}

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
@@ -667,3 +667,13 @@ TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
667667
JMP libc_utimensat(SB)
668668
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4
669669
DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB)
670+
671+
TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0
672+
JMP libc_pledge(SB)
673+
GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4
674+
DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB)
675+
676+
TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0
677+
JMP libc_unveil(SB)
678+
GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4
679+
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
@@ -667,3 +667,13 @@ TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
667667
JMP libc_utimensat(SB)
668668
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8
669669
DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB)
670+
671+
TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0
672+
JMP libc_pledge(SB)
673+
GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8
674+
DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB)
675+
676+
TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0
677+
JMP libc_unveil(SB)
678+
GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8
679+
DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB)

0 commit comments

Comments
 (0)