summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-03-06 20:42:15 -0800
committerAndrew G. Morgan <morgan@kernel.org>2021-03-06 20:42:52 -0800
commitda8358234e1cc8324b4046dd5c92ae24f3fbd485 (patch)
tree7789437282cd64d6709d77d4e921bc8932f0905b
parentcb1f3fd143b95f9be6605d69256ae0438c83b84d (diff)
downloadlibcap2-da8358234e1cc8324b4046dd5c92ae24f3fbd485.tar.gz
Provide a pair of cap.Prctl{w,}() convenience functionscap/v1.2.49-rc1
It can be difficult to use the syscall Prctl() with POSIX semantics, so provide a function cap.Prctlw() that mirrors the call on all OS threads of the runtime. cap.Prctl() is provided for convenience. It should be used when reading state through the Prctl system call, or when setting state that is naturally shared by the whole process. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r--cap/cap_test.go12
-rw-r--r--cap/convenience.go37
2 files changed, 39 insertions, 10 deletions
diff --git a/cap/cap_test.go b/cap/cap_test.go
index a8c67a7..db4d61c 100644
--- a/cap/cap_test.go
+++ b/cap/cap_test.go
@@ -226,22 +226,14 @@ func TestFuncLaunch(t *testing.T) {
}
if _, err := FuncLauncher(func(data interface{}) error {
- // This function is super contrived, since we want to
- // do something 'privileged' without necessarily
- // having any privilege. We may at some point provide
- // a convenience wrapper, cap.Prctlw(), and then we can
- // clean this test case up to use that.
- state, sc := scwStateSC()
- defer scwSetState(launchBlocked, state, -1)
-
was, ok := data.(int)
if !ok {
return fmt.Errorf("data was not an int: %v", data)
}
- if _, err := sc.prctlwcall(prSetKeepCaps, uintptr(1-was), 0); err != nil {
+ if _, err := Prctlw(prSetKeepCaps, uintptr(1-was)); err != nil {
return err
}
- if v, err := sc.prctlrcall(prGetKeepCaps, 0, 0); err != nil {
+ if v, err := Prctl(prGetKeepCaps); err != nil {
return err
} else if v == was {
return fmt.Errorf("PR_KEEP_CAPS unchanged: got=%d, want=%v", v, 1-was)
diff --git a/cap/convenience.go b/cap/convenience.go
index dde7af8..0a67019 100644
--- a/cap/convenience.go
+++ b/cap/convenience.go
@@ -2,6 +2,7 @@ package cap
import (
"errors"
+ "fmt"
"syscall"
"unsafe"
)
@@ -291,3 +292,39 @@ func SetGroups(gid int, suppl ...int) error {
defer scwSetState(launchBlocked, state, -1)
return sc.setGroups(gid, suppl)
}
+
+//go:unintptrescapes
+
+// Prctlw is a convenience function for performing a syscall.Prctl()
+// call that executes on all the threads of the process. It is called
+// Prctlw because it is only appropriate to call this function when it
+// is writing thread state that the caller wants to set on all OS
+// threads of the process to observe POSIX semantics when Linux
+// doesn't natively honor them. (Check prctl documentation for when it
+// is appropriate to use this vs. a normal syscall.Prctl() call.)
+func Prctlw(prVal uintptr, args ...uintptr) (int, error) {
+ if n := len(args); n > 5 {
+ return -1, fmt.Errorf("prctl supports up to 5 arguments (not %d)", n)
+ }
+ state, sc := scwStateSC()
+ defer scwSetState(launchBlocked, state, -1)
+ as := make([]uintptr, 5)
+ copy(as, args)
+ return sc.prctlwcall6(prVal, as[0], as[1], as[2], as[3], as[4])
+}
+
+//go:unintptrescapes
+
+// Prctl is a convenience function that performs a syscall.Prctl()
+// that either reads state using a single OS thread, or performs a
+// Prctl that is treated as a process wide setting. It is provided for
+// symmetry reasons, but is equivalent to simply calling the
+// corresponding syscall function.
+func Prctl(prVal uintptr, args ...uintptr) (int, error) {
+ if n := len(args); n > 5 {
+ return -1, fmt.Errorf("prctl supports up to 5 arguments (not %d)", n)
+ }
+ as := make([]uintptr, 5)
+ copy(as, args)
+ return singlesc.prctlrcall6(prVal, as[0], as[1], as[2], as[3], as[4])
+}