-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
syscall: Setuid/Setgid doesn't apply to all threads on Linux #1435
Comments
Comment 1 by m@capitanio.org: Well, you just must just put the gorutine creation after the suid/sguid call. Isn't that what you would actually expect? ... en = syscall.Setuid(uid) if en != 0 { fmt.Println("Setuid error:", os.Errno(en)) os.Exit(1) } for ii := 1; ii < 10; ii++ { go printIds(ii) time.Sleep(1e8) } printIds(0) sudo -i export GOMAXPROCS=2 /tmp/setuid 65534 65534 gorutine 1: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 2: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 3: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 4: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 5: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 6: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 7: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 8: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 9: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 0: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 1: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 2: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 3: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 4: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 5: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 6: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 7: uid=65534 euid=65534 gid=65534 egid=65534 gorutine 8: uid=65534 euid=65534 gid=65534 egid=65534 |
A more concrete example of this is perhaps: http://golang.org/src/pkg/time/tick.go To be safe, one would have to check the code of every package you import to be sure that you don't inadvertently call a function that starts a goroutine before you get round to calling Setuid. |
Comment 4 by m@capitanio.org: You could use runtime.Goroutines() to wait if you are the only one. If it isn't possible to terminate all "someHousekeepingFunc()" e.g. with Goexit(), I think setuid gains you to security not much ... |
Comment 5 by ziutek@Lnet.pl: Example code which shows how I discovered this problem: package main import ( "os" "net" "http" "fmt" "syscall" "log" ) type Handler string func (hh *Handler) ServeHTTP(con http.ResponseWriter, req *http.Request) { fmt.Fprint(con, "Hello! I am %s. My UID/GID is %d/%d. Bye!\n", *hh, syscall.Getuid(), syscall.Getgid()) } func main() { // To listen on port 80 we need root privileges ls, err := net.Listen("tcp", "127.0.0.1:80") if err != nil { log.Exitln("Can't listen:", err) } // We don't need root privileges any more if en := syscall.Setgid(65534); en != 0 { log.Exitln("Setgid error:", os.Errno(en)) } if en := syscall.Setuid(65534); en != 0 { log.Exitln("Setuid error:", os.Errno(en)) } // Run http service without root privileges handler := Handler("Test handler") if err = http.Serve(ls, &handler); err != nil { log.Exitln("Http server:", err) } } Compile it and run as root. Then use ps -efL to show threads: UID PID PPID LWP C NLWP STIME TTY TIME CMD nobody 32401 32333 32401 0 2 18:48 pts/2 00:00:00 ./http root 32401 32333 32402 0 2 18:48 pts/2 00:00:00 ./http There is two threads. But I didn't create any gorutine explicitly. Use curl to send request to the application: $ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 65534/65534. Bye! Now ps shows: UID PID PPID LWP C NLWP STIME TTY TIME CMD nobody 32401 32333 32401 0 3 18:48 pts/2 00:00:00 ./http root 32401 32333 32402 0 3 18:48 pts/2 00:00:00 ./http nobody 32401 32333 32406 0 3 18:49 pts/2 00:00:00 ./http Use siege stress test: $ siege 127.0.0.1 -c25 -d0 -t 10s ** SIEGE 2.70 ** Preparing 25 concurrent users for battle. The server is now under siege... Lifting the server siege.. done. Transactions: 12543 hits Availability: 100.00 % Elapsed time: 9.99 secs Data transferred: 0.65 MB Response time: 0.02 secs Transaction rate: 1255.56 trans/sec Throughput: 0.06 MB/sec Concurrency: 24.79 Successful transactions: 12544 Failed transactions: 0 Longest transaction: 0.04 Shortest transaction: 0.01 Now ps shows: UID PID PPID LWP C NLWP STIME TTY TIME CMD nobody 32401 32333 32401 0 11 18:48 pts/2 00:00:01 ./http root 32401 32333 32402 0 11 18:48 pts/2 00:00:01 ./http nobody 32401 32333 32406 0 11 18:49 pts/2 00:00:01 ./http nobody 32401 32333 32553 0 11 18:51 pts/2 00:00:00 ./http nobody 32401 32333 32554 2 11 18:51 pts/2 00:00:01 ./http root 32401 32333 32555 3 11 18:51 pts/2 00:00:01 ./http root 32401 32333 32556 3 11 18:51 pts/2 00:00:01 ./http root 32401 32333 32557 0 11 18:51 pts/2 00:00:00 ./http root 32401 32333 32558 3 11 18:51 pts/2 00:00:01 ./http nobody 32401 32333 32559 3 11 18:51 pts/2 00:00:01 ./http nobody 32401 32333 32560 3 11 18:51 pts/2 00:00:01 ./http Use curl a few times: $ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/0. Bye! $ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/0. Bye! $ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/0. Bye! It's nice! I talking with root on your server! |
Comment 6 by m@capitanio.org: Oh, you are right. There is currently no official way to use a net socket without spawning 2nd goroutine (EpollWait): goroutine 2 [3]: runtime.entersyscall+0x28 /data4/soft/go/go/src/pkg/runtime/proc.c:577 runtime.entersyscall() syscall.Syscall6+0x5 /data4/soft/go/go/src/pkg/syscall/asm_linux_amd64.s:40 syscall.Syscall6() syscall.EpollWait+0x8d /data4/soft/go/go/src/pkg/syscall/zsyscall_linux_amd64.go:188 syscall.EpollWait(0x7fda00000006, 0x7fda62d566a0, 0x100000001, 0xffffffff, 0xc, ...) net.*pollster·WaitFD+0xfe /data4/soft/go/go/src/pkg/net/fd_linux.go:116 net.*pollster·WaitFD(0x7fda62d564d0, 0x0, 0x0, 0x0, 0x0, ...) net.*pollServer·Run+0xa3 /data4/soft/go/go/src/pkg/net/fd.go:207 net.*pollServer·Run(0x7fda62d20600, 0x0) runtime.goexit /data4/soft/go/go/src/pkg/runtime/proc.c:149 runtime.goexit() but opening a file descriptor works: f, err := os.Open("/etc/shadow", os.O_RDONLY | os.O_SYNC , 0666) fmt.Println(runtime.Goroutines()) ... syscall.Setuid(uid) ... syscall.Setgid(gid) var buf [20]byte _, err = f.Read(buf[:]); fmt.Println(buf) time.Sleep(100e9) ./run open /etc/shadow: permission denied sudo ./run 1 [114 111 111 ... ps -efL|grep run mc 14097 24745 14097 0 1 19:36 pts/4 00:00:00 /run 1001 1001 |
Owner changed to r...@golang.org. Status changed to Accepted. |
The syscall is doing what it advertises: it invokes the Linux system call. And the Linux system call only affects the calling thread (!), confirmed by reading the sources. I was surprised to find that setuid works when called from a C Linux pthreads (NPTL) program, though. So I investigated further. It turns out that glibc's setuid sends a signal to every other thread to cause them to invoke the system call too. http://goo.gl/8zf3C - called by setuid http://goo.gl/CcXyX - signal handler I suppose Go is going to need to do this at some point, as part of implementing os.Setuid, os.Setgid, etc. What a crock. For now you can work around this by calling runtime.LockOSThread. That locks the goroutine onto its current OS thread, so that it only runs in that thread and that thread only runs that goroutine. Then you can call Setuid Setgid etc and also ForkExec. Status changed to LongTerm. |
Comment 9 by ziutek@Lnet.pl: If I add runtime.LockOSThread in my second example, just before Setgid, I get strange behavior: michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 65534/0. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/65534. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/0. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 65534/65534. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 65534/65534. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 65534/0. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 65534/65534. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/0. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 65534/65534. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/0. Bye! michal@md-lap:~$ curl 127.0.0.1 Hello! I am Test handler. My UID/GID is 0/65534. Bye! Attachments:
|
Comment 10 by ziutek@Lnet.pl: Sorry. I had not noticed it before, but this is also when there is no runtime.LockOSThread call. This is probably due to rescheduling between syscall.Getuid() and syscall.Getgid(). |
Comment 12 by ziutek@Lnet.pl: I probably found workaround for my web application: package main import ( "os" "net" "http" "fmt" "syscall" "runtime" "log" ) func lockUidGid(new_uid, new_gid int) { runtime.LockOSThread() uid := syscall.Getuid() gid := syscall.Getgid() if uid == new_uid && gid == new_gid { return } if en := syscall.Setgid(new_uid); en != 0 { log.Exitln("Setgid error:", os.Errno(en)) } if en := syscall.Setuid(new_gid); en != 0 { log.Exitln("Setuid error:", os.Errno(en)) } } type Handler string func (hh *Handler) ServeHTTP(con http.ResponseWriter, req *http.Request) { lockUidGid(65534, 65534) fmt.Fprintf(con, "Hello! I am %s. My UID/GID is %d/%d. Bye!\n", *hh, syscall.Getuid(), syscall.Getgid()) } func main() { // To listen on port 80 we need root privileges ls, err := net.Listen("tcp", "127.0.0.1:80") if err != nil { log.Exitln("Can't listen:", err) } // We don't need root privileges any more lockUidGid(65534, 65534) // Run http service without root privileges handler := Handler("Test handler") if err = http.Serve(ls, &handler); err != nil { log.Exitln("Http server:", err) } } After running siege there is no threads with root privileges: $ siege 127.0.0.1 -c25 -d0 -t10s ** SIEGE 2.69 ** Preparing 25 concurrent users for battle. The server is now under siege... Lifting the server siege.. done. Transactions: 28782 hits Availability: 100.00 % Elapsed time: 9.79 secs Data transferred: 1.59 MB Response time: 0.01 secs Transaction rate: 2939.94 trans/sec Throughput: 0.16 MB/sec Concurrency: 24.86 Successful transactions: 28782 Failed transactions: 0 Longest transaction: 0.04 Shortest transaction: 0.00 $ ps -efL|egrep 'http|UID'|egrep -v egrep UID PID PPID LWP C NLWP STIME TTY TIME CMD nobody 4109 2928 4109 1 10 23:00 pts/1 00:00:04 ./http nobody 4109 2928 4110 0 10 23:00 pts/1 00:00:02 ./http nobody 4109 2928 4112 0 10 23:00 pts/1 00:00:02 ./http nobody 4109 2928 4153 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4154 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4155 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4156 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4157 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4158 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4159 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4109 1 10 23:00 pts/1 00:00:04 ./http nobody 4109 2928 4110 0 10 23:00 pts/1 00:00:02 ./http nobody 4109 2928 4112 0 10 23:00 pts/1 00:00:02 ./http nobody 4109 2928 4153 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4154 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4155 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4156 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4157 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4158 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4159 0 10 23:01 pts/1 00:00:01 ./http Thanks! |
Comment 13 by ziutek@Lnet.pl: I probably found workaround for my web application: package main import ( "os" "net" "http" "fmt" "syscall" "runtime" "log" ) func lockUidGid(new_uid, new_gid int) { runtime.LockOSThread() uid := syscall.Getuid() gid := syscall.Getgid() if uid == new_uid && gid == new_gid { return } if en := syscall.Setgid(new_uid); en != 0 { log.Exitln("Setgid error:", os.Errno(en)) } if en := syscall.Setuid(new_gid); en != 0 { log.Exitln("Setuid error:", os.Errno(en)) } } type Handler string func (hh *Handler) ServeHTTP(con http.ResponseWriter, req *http.Request) { lockUidGid(65534, 65534) fmt.Fprintf(con, "Hello! I am %s. My UID/GID is %d/%d. Bye!\n", *hh, syscall.Getuid(), syscall.Getgid()) } func main() { // To listen on port 80 we need root privileges ls, err := net.Listen("tcp", "127.0.0.1:80") if err != nil { log.Exitln("Can't listen:", err) } // We don't need root privileges any more lockUidGid(65534, 65534) // Run http service without root privileges handler := Handler("Test handler") if err = http.Serve(ls, &handler); err != nil { log.Exitln("Http server:", err) } } After running siege there is no threads with root privileges: $ siege 127.0.0.1 -c25 -d0 -t10s ** SIEGE 2.69 ** Preparing 25 concurrent users for battle. The server is now under siege... Lifting the server siege.. done. Transactions: 28782 hits Availability: 100.00 % Elapsed time: 9.79 secs Data transferred: 1.59 MB Response time: 0.01 secs Transaction rate: 2939.94 trans/sec Throughput: 0.16 MB/sec Concurrency: 24.86 Successful transactions: 28782 Failed transactions: 0 Longest transaction: 0.04 Shortest transaction: 0.00 $ ps -efL|egrep 'http|UID'|egrep -v egrep UID PID PPID LWP C NLWP STIME TTY TIME CMD nobody 4109 2928 4109 1 10 23:00 pts/1 00:00:04 ./http nobody 4109 2928 4110 0 10 23:00 pts/1 00:00:02 ./http nobody 4109 2928 4112 0 10 23:00 pts/1 00:00:02 ./http nobody 4109 2928 4153 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4154 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4155 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4156 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4157 0 10 23:01 pts/1 00:00:00 ./http nobody 4109 2928 4158 0 10 23:01 pts/1 00:00:01 ./http nobody 4109 2928 4159 0 10 23:01 pts/1 00:00:01 ./http Thanks! |
It's still not guaranteed that future goroutines won't have the original 0/0 uid/gid. Obviously if all tasks have been switched then you're safe but there's no guarantee that will switch all the tasks. Using the network capability is much safer if you are worried about this kind of thing. Russ |
Comment 15 by m@capitanio.org: >For now you can work around this by calling runtime.LockOSThread. runtime.GOMAXPROCS(1) runtime.LockOSThread() ls, _ := net.Listen("tcp", "localhost:42") syscall.Setuid(uid) http.Serve(ls, nil) Was that what you meant? It's actually impossible ;) There is always +1 thread spawned by Listen that keeps running as root. ps -efL| grep setuid mc 19213 24745 19213 0 2 22:41 pts/4 00:00:00 /tmp/setuid 1001 1001 root 19213 24745 19215 0 2 22:41 pts/4 00:00:00 /tmp/setuid 1001 1001 ^\SIGQUIT: quit PC=0x411b25 runtime.futex+0x23 /data4/soft/go/go/src/pkg/runtime/linux/amd64/sys.s:137 runtime.futex() futexsleep+0x50 /data4/soft/go/go/src/pkg/runtime/linux/thread.c:51 futexsleep(0x664c18, 0x300000003, 0x0, 0x0) futexlock+0x85 /data4/soft/go/go/src/pkg/runtime/linux/thread.c:119 futexlock(0x664c18, 0x100000000) runtime.notesleep+0x25 /data4/soft/go/go/src/pkg/runtime/linux/thread.c:204 runtime.notesleep(0x664c18, 0x7fffa0812498) nextgandunlock+0x146 /data4/soft/go/go/src/pkg/runtime/proc.c:343 nextgandunlock() scheduler+0x16f /data4/soft/go/go/src/pkg/runtime/proc.c:536 scheduler() runtime.mstart+0x74 /data4/soft/go/go/src/pkg/runtime/proc.c:393 runtime.mstart() _rt0_amd64+0x95 /data4/soft/go/go/src/pkg/runtime/amd64/asm.s:69 _rt0_amd64() goroutine 2 [3]: runtime.entersyscall+0x28 /data4/soft/go/go/src/pkg/runtime/proc.c:577 runtime.entersyscall() syscall.Syscall6+0x5 /data4/soft/go/go/src/pkg/syscall/asm_linux_amd64.s:40 syscall.Syscall6() syscall.EpollWait+0x8d /data4/soft/go/go/src/pkg/syscall/zsyscall_linux_amd64.go:188 syscall.EpollWait(0x7f6600000006, 0x7f669eb58950, 0x100000001, 0xffffffff, 0xc, ...) net.*pollster·WaitFD+0xfe /data4/soft/go/go/src/pkg/net/fd_linux.go:116 net.*pollster·WaitFD(0x7f669eb587a0, 0x0, 0x6400000000, 0x0, 0x0, ...) net.*pollServer·Run+0xa3 /data4/soft/go/go/src/pkg/net/fd.go:207 net.*pollServer·Run(0x7f669eb27600, 0x0) runtime.goexit /data4/soft/go/go/src/pkg/runtime/proc.c:149 runtime.goexit() goroutine 1 [4]: runtime.gosched+0x77 /data4/soft/go/go/src/pkg/runtime/proc.c:558 runtime.gosched() runtime.chanrecv+0x18b /data4/soft/go/go/src/pkg/runtime/chan.c:364 runtime.chanrecv(0x7f669eb3eea0, 0x7f669eb13da8, 0x0, 0x0, 0x0, ...) runtime.chanrecv1+0x41 /data4/soft/go/go/src/pkg/runtime/chan.c:444 runtime.chanrecv1(0x7f669eb3eea0, 0x7f669eb123c0) net.*pollServer·WaitRead+0x52 /data4/soft/go/go/src/pkg/net/fd.go:247 net.*pollServer·WaitRead(0x7f669eb27600, 0x7f669eb123c0, 0x0, 0x0) net.*netFD·accept+0x39a /data4/soft/go/go/src/pkg/net/fd.go:579 net.*netFD·accept(0x7f669eb123c0, 0x43efe8, 0x0, 0x0, 0x0, ...) net.*TCPListener·AcceptTCP+0x71 /data4/soft/go/go/src/pkg/net/tcpsock.go:261 net.*TCPListener·AcceptTCP(0x7f669eb0f178, 0x7f669eb13ee0, 0x0, 0x0, 0x1007f6600000001, ...) net.*TCPListener·Accept+0x49 /data4/soft/go/go/src/pkg/net/tcpsock.go:271 net.*TCPListener·Accept(0x7f669eb0f178, 0x0, 0x0, 0x0, 0x0, ...) http.Serve+0x7a /data4/soft/go/go/src/pkg/http/server.go:665 http.Serve(0x7f669eb27b40, 0x7f669eb0f178, 0x7f669eb4b030, 0x7f669eb0f0a8, 0x0, ...) main.main+0x886 /home/mc/server/go/tests/setuid2.go:73 main.main() runtime.mainstart+0xf /data4/soft/go/go/src/pkg/runtime/amd64/asm.s:77 runtime.mainstart() runtime.goexit /data4/soft/go/go/src/pkg/runtime/proc.c:149 runtime.goexit() rax 0xfffffffffffffffc rbx 0x664c18 rcx 0xffffffffffffffff rdx 0x3 rdi 0x664c18 rsi 0x0 rbp 0x7f669eb13c98 rsp 0x7fffa08123e8 r8 0x0 r9 0x0 r10 0x6601b0 r11 0x206 r12 0x250 r13 0x7fffa0812500 r14 0x0 r15 0x0 rip 0x411b25 rflags 0x206 cs 0x33 fs 0x0 gs 0x0 Trace/breakpoint trap |
Comment 17 by m@capitanio.org: Thanks, I didn't hit the reload button and missed the comments. BTW, the crock design is elaborated here ;) http://www.cs.utexas.edu/~witchel/372/lectures/POSIX_Linux_Threading.pdf |
Comment 18 by ziutek@Lnet.pl: I suggest put a clear warning in the header of syscall package about issues that may cause the use of this package. Maybe a list of links to known issues for each function would not be a bad idea... |
Comment 23 by tsar@cosmocode.de: Is anyone working on that? If there are some experimental patches I would be happy to test them ;-) I added a little test case to my own little library to detect non-posix compliant systems (all the *bsd variants work as expected): https://github.com/sarnowski/mitigation |
I'm looking into this, I have a start to the implementation of what rsc suggested above. I've updated the example for go1, along with using the os.Setuid/os.Setgid (an API change) from my soon-to-be-posted CL. Attachments:
|
The cause of this problem seems closely related to the cause of daemonization problems; that is, the inability to consistently execute package main code on a vanilla runtime before any thread spawning occurs (as was trivial with the old init behavior), or alternatively, the ability to command the runtime to suspend goroutines at their next non-externally-blocked scheduling point, closing all but one thread (if the runtime can even temporarily coexist with app code in the main thread). Preemptive scheduling would certainly make the latter simpler. |
The program web.go uses "libcap/cap" to raise and lower capabilities in order to bind to a privileged port. Writing this code, I now realize that Go's runtime is not really suited to minimal privilege guarantees. The code does raise and lower the effective capability Value needed, but to be fully robust, we're going to have to wait for the following issue with the Go runtime to find a resolution: golang/go#1435 Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
I've moved my go.patch to address: golang/go#1435 into a development patch against the upstream Go sources: https://go-review.googlesource.com/c/go/+/210639/ and the review process will likely evolve it somewhat. I plan to ensure that working libcap/cap Go package is in sync with the working state of the above development change. As such, there is no need to keep the patch here any more. I'll keep the tests for now, as it isn't clear to me how the Go source tree supports tests that require privilege yet. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
This is something pretty fundamental that a number of folk have asked about. It is essentially the motivating issue for: golang/go#1435 Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
On FreeBSD, changing a process's supplementary groups with setgroups(2) will also change the egid of the process, setting it to the first entry in the provided list. This is distinct from the behaviour on other platforms (and possibly a violation of the POSIX standard). Because of this, our incubator code wouldn't change the process's gid, because it would read the newly-changed egid, compare it against the expected egid, and since they matched, not change the gid. Fix this by ensuring that the setGroups call behaves the same on FreeBSD as it does on other platforms by adding the current egid as the first entry in the list, and thus not changing it unexpectedly. Also, while we're here... defensively call runtime.LockOSThread to make it less likely we trip over bugs like golang/go#1435 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Icc08c629ddc87923aa9ae0d542f63925e9c40cd3
On FreeBSD, changing a process's supplementary groups with setgroups(2) will also change the egid of the process, setting it to the first entry in the provided list. This is distinct from the behaviour on other platforms (and possibly a violation of the POSIX standard). Because of this, our incubator code wouldn't change the process's gid, because it would read the newly-changed egid, compare it against the expected egid, and since they matched, not change the gid. This could be observed by running "id -p" in two contexts. The expected output, and the output returned when running from a SSH shell, is: andrew@freebsd:~ $ id -p uid andrew groups andrew However, when run via "ssh andrew@freebsd id -p", the output would be: $ ssh andrew@freebsd id -p login root uid andrew rgid wheel groups andrew (this could also be observed via "id -g -r" to print just the gid) We fix this by ensuring that the setGroups call behaves the same on FreeBSD as it does on other platforms by adding the current egid as the first entry in the list, and thus not changing it unexpectedly. Also, while we're here... defensively call runtime.LockOSThread to make it less likely we trip over bugs like golang/go#1435 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Icc08c629ddc87923aa9ae0d542f63925e9c40cd3
On FreeBSD, changing a process's supplementary groups with setgroups(2) will also change the egid of the process, setting it to the first entry in the provided list. This is distinct from the behaviour on other platforms (and possibly a violation of the POSIX standard). Because of this, our incubator code wouldn't change the process's gid, because it would read the newly-changed egid, compare it against the expected egid, and since they matched, not change the gid. This could be observed by running "id -p" in two contexts. The expected output, and the output returned when running from a SSH shell, is: andrew@freebsd:~ $ id -p uid andrew groups andrew However, when run via "ssh andrew@freebsd id -p", the output would be: $ ssh andrew@freebsd id -p login root uid andrew rgid wheel groups andrew (this could also be observed via "id -g -r" to print just the gid) We fix this by ensuring that the setGroups call behaves the same on FreeBSD as it does on other platforms by adding the current egid as the first entry in the list, and thus not changing it unexpectedly. More information can be found in the following article: https://www.usenix.org/system/files/login/articles/325-tsafrir.pdf Also, while we're here... defensively call runtime.LockOSThread to make it less likely we trip over bugs like golang/go#1435 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Icc08c629ddc87923aa9ae0d542f63925e9c40cd3
On FreeBSD, changing a process's supplementary groups with setgroups(2) will also change the egid of the process, setting it to the first entry in the provided list. This is distinct from the behaviour on other platforms (and possibly a violation of the POSIX standard). Because of this, our incubator code wouldn't change the process's gid, because it would read the newly-changed egid, compare it against the expected egid, and since they matched, not change the gid. This could be observed by running "id -p" in two contexts. The expected output, and the output returned when running from a SSH shell, is: andrew@freebsd:~ $ id -p uid andrew groups andrew However, when run via "ssh andrew@freebsd id -p", the output would be: $ ssh andrew@freebsd id -p login root uid andrew rgid wheel groups andrew (this could also be observed via "id -g -r" to print just the gid) We fix this by ensuring that the setGroups call behaves the same on FreeBSD as it does on other platforms by adding the current egid as the first entry in the list, and thus not changing it unexpectedly. More information can be found in the following article: https://www.usenix.org/system/files/login/articles/325-tsafrir.pdf Also, while we're here... defensively call runtime.LockOSThread to make it less likely we trip over bugs like golang/go#1435 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Icc08c629ddc87923aa9ae0d542f63925e9c40cd3
On FreeBSD and Darwin, changing a process's supplementary groups with setgroups(2) will also change the egid of the process, setting it to the first entry in the provided list. This is distinct from the behaviour on other platforms (and possibly a violation of the POSIX standard). Because of this, our incubator code wouldn't change the process's gid, because it would read the newly-changed egid, compare it against the expected egid, and since they matched, not change the gid. This could be observed by running "id -p" in two contexts. The expected output, and the output returned when running from a SSH shell, is: andrew@freebsd:~ $ id -p uid andrew groups andrew However, when run via "ssh andrew@freebsd id -p", the output would be: $ ssh andrew@freebsd id -p login root uid andrew rgid wheel groups andrew (this could also be observed via "id -g -r" to print just the gid) We fix this by ensuring that the setGroups call behaves the same on FreeBSD as it does on other platforms by adding the current egid as the first entry in the list, and thus not changing it unexpectedly. More information can be found in the following article: https://www.usenix.org/system/files/login/articles/325-tsafrir.pdf Also, while we're here... defensively call runtime.LockOSThread to make it less likely we trip over bugs like golang/go#1435 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Icc08c629ddc87923aa9ae0d542f63925e9c40cd3
The |
Sorry, we don't try to set the milestone when we close an issue. But, yes, this was fixed in the 1.16 release. |
This makes it less likely that we trip over bugs like golang/go#1435. Updates #7616 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ic28c03c3ad8ed5274a795c766b767fa876029f0e
This makes it less likely that we trip over bugs like golang/go#1435. Updates #7616 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ic28c03c3ad8ed5274a795c766b767fa876029f0e
This makes it less likely that we trip over bugs like golang/go#1435. Updates #7616 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ic28c03c3ad8ed5274a795c766b767fa876029f0e
This makes it less likely that we trip over bugs like golang/go#1435. Updates tailscale#7616 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ic28c03c3ad8ed5274a795c766b767fa876029f0e
Since Go 1.16, [Go issue 1435][1] is solved, and the stdlib syscall implementations work on Linux. While they are a bit more flexible/heavier-weight than the implementations that were copied to libcontainer/system (working across all threads), we compile with Cgo, and using the libc wrappers should be just as suitable. [1]: golang/go#1435 Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Since Go 1.16, [Go issue 1435][1] is solved, and the stdlib syscall implementations work on Linux. While they are a bit more flexible/heavier-weight than the implementations that were copied to libcontainer/system (working across all threads), we compile with Cgo, and using the libc wrappers should be just as suitable. [1]: golang/go#1435 Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Since Go 1.16, [Go issue 1435][1] is solved, and the stdlib syscall implementations work on Linux. While they are a bit more flexible/heavier-weight than the implementations that were copied to libcontainer/system (working across all threads), we compile with Cgo, and using the libc wrappers should be just as suitable. [1]: golang/go#1435 Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Since Go 1.16, [Go issue 1435][1] is solved, and the stdlib syscall implementations work on Linux. [1]: golang/go#1435 Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Since Go 1.16, [Go issue 1435][1] is solved, and the stdlib syscall implementations work on Linux. [1]: golang/go#1435 Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Since Go 1.16, [Go issue 1435][1] is solved, and the stdlib syscall implementations work on Linux. [1]: golang/go#1435 Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
by ziutek@Lnet.pl:
Attachments:
The text was updated successfully, but these errors were encountered: