From 80a651a19474c78b1b2716cbf8c85ffd78698823 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sat, 6 Feb 2021 11:13:22 +0100 Subject: [PATCH] syscallcompat: MknodatUser: work around changed syscall.Setgroups semantics Since go1.16beta1 (commit d1b1145cace8b968307f9311ff611e4bb810710c , https://go-review.googlesource.com/c/go/+/210639 ) syscall.{Setgroups,Setregid,Setreuid} affects all threads, which is exactly what we not want. We now use unix.{Setgroups,Setregid,Setreuid} instead. Workarounds https://github.com/golang/go/issues/1435 . --- internal/syscallcompat/sys_linux.go | 70 ++++++++++++++++++----------- test.bash | 9 +++- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 06b9696..d178323 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -92,28 +92,38 @@ func getSupplementaryGroups(pid uint32) (gids []int) { } // OpenatUser runs the Openat syscall in the context of a different user. +// +// It switches the current thread to the new user, performs the syscall, +// and switches back. func OpenatUser(dirfd int, path string, flags int, mode uint32, context *fuse.Context) (fd int, err error) { if context != nil { runtime.LockOSThread() defer runtime.UnlockOSThread() - err = syscall.Setgroups(getSupplementaryGroups(context.Pid)) - if err != nil { - return -1, err - } - defer syscall.Setgroups(nil) + // Since go1.16beta1 (commit d1b1145cace8b968307f9311ff611e4bb810710c , + // https://go-review.googlesource.com/c/go/+/210639 ) + // syscall.{Setgroups,Setregid,Setreuid} affects all threads, which + // is exactly what we not want. + // + // We now use unix.{Setgroups,Setregid,Setreuid} instead. - err = syscall.Setregid(-1, int(context.Owner.Gid)) + err = unix.Setgroups(getSupplementaryGroups(context.Pid)) if err != nil { return -1, err } - defer syscall.Setregid(-1, 0) + defer unix.Setgroups(nil) - err = syscall.Setreuid(-1, int(context.Owner.Uid)) + err = unix.Setregid(-1, int(context.Owner.Gid)) if err != nil { return -1, err } - defer syscall.Setreuid(-1, 0) + defer unix.Setregid(-1, 0) + + err = unix.Setreuid(-1, int(context.Owner.Uid)) + if err != nil { + return -1, err + } + defer unix.Setreuid(-1, 0) } return Openat(dirfd, path, flags, mode) @@ -125,28 +135,30 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { } // MknodatUser runs the Mknodat syscall in the context of a different user. +// +// See OpenatUser() for how this works. func MknodatUser(dirfd int, path string, mode uint32, dev int, context *fuse.Context) (err error) { if context != nil { runtime.LockOSThread() defer runtime.UnlockOSThread() - err = syscall.Setgroups(getSupplementaryGroups(context.Pid)) + err = unix.Setgroups(getSupplementaryGroups(context.Pid)) if err != nil { return err } - defer syscall.Setgroups(nil) + defer unix.Setgroups(nil) - err = syscall.Setregid(-1, int(context.Owner.Gid)) + err = unix.Setregid(-1, int(context.Owner.Gid)) if err != nil { return err } - defer syscall.Setregid(-1, 0) + defer unix.Setregid(-1, 0) - err = syscall.Setreuid(-1, int(context.Owner.Uid)) + err = unix.Setreuid(-1, int(context.Owner.Uid)) if err != nil { return err } - defer syscall.Setreuid(-1, 0) + defer unix.Setreuid(-1, 0) } return Mknodat(dirfd, path, mode, dev) @@ -193,56 +205,60 @@ func FchmodatNofollow(dirfd int, path string, mode uint32) (err error) { } // SymlinkatUser runs the Symlinkat syscall in the context of a different user. +// +// See OpenatUser() for how this works. func SymlinkatUser(oldpath string, newdirfd int, newpath string, context *fuse.Context) (err error) { if context != nil { runtime.LockOSThread() defer runtime.UnlockOSThread() - err = syscall.Setgroups(getSupplementaryGroups(context.Pid)) + err = unix.Setgroups(getSupplementaryGroups(context.Pid)) if err != nil { return err } - defer syscall.Setgroups(nil) + defer unix.Setgroups(nil) - err = syscall.Setregid(-1, int(context.Owner.Gid)) + err = unix.Setregid(-1, int(context.Owner.Gid)) if err != nil { return err } - defer syscall.Setregid(-1, 0) + defer unix.Setregid(-1, 0) - err = syscall.Setreuid(-1, int(context.Owner.Uid)) + err = unix.Setreuid(-1, int(context.Owner.Uid)) if err != nil { return err } - defer syscall.Setreuid(-1, 0) + defer unix.Setreuid(-1, 0) } return Symlinkat(oldpath, newdirfd, newpath) } // MkdiratUser runs the Mkdirat syscall in the context of a different user. +// +// See OpenatUser() for how this works. func MkdiratUser(dirfd int, path string, mode uint32, caller *fuse.Caller) (err error) { if caller != nil { runtime.LockOSThread() defer runtime.UnlockOSThread() - err = syscall.Setgroups(getSupplementaryGroups(caller.Pid)) + err = unix.Setgroups(getSupplementaryGroups(caller.Pid)) if err != nil { return err } - defer syscall.Setgroups(nil) + defer unix.Setgroups(nil) - err = syscall.Setregid(-1, int(caller.Gid)) + err = unix.Setregid(-1, int(caller.Gid)) if err != nil { return err } - defer syscall.Setregid(-1, 0) + defer unix.Setregid(-1, 0) - err = syscall.Setreuid(-1, int(caller.Uid)) + err = unix.Setreuid(-1, int(caller.Uid)) if err != nil { return err } - defer syscall.Setreuid(-1, 0) + defer unix.Setreuid(-1, 0) } return Mkdirat(dirfd, path, mode) diff --git a/test.bash b/test.bash index ce2d392..266a875 100755 --- a/test.bash +++ b/test.bash @@ -80,7 +80,14 @@ else fi if grep -R "panic(" ./*.go internal ; then - echo "Please use log.Panic instead of naked panic!" + echo "$MYNAME: Please use log.Panic instead of naked panic!" + exit 1 +fi + +# All functions from the commit msg in https://go-review.googlesource.com/c/go/+/210639 +if grep -R -E 'syscall.(Setegid|Seteuid|Setgroups|Setgid|Setregid|Setreuid|Setresgid|Setresuid|Setuid)\(' \ + ./*.go internal ; then + echo "$MYNAME: You probably want to use unix.Setgroups and friends. See the comments in OpenatUser() for why." exit 1 fi