Hacker News new | ask | show | jobs
by mjquinn 4197 days ago
As a quick word of caution (which doesn't invalidate anything you've said), Go has a long-standing bug[0] whereby syscall.Setuid doesn't always apply to all threads (on Linux at least) so extra care does have to be taken.

[0] https://github.com/golang/go/issues/1435

3 comments

Wow. That's a fun one.

It's also a perfect example of why even really amazing teams reinventing a language/tooling ecosystem from scratch stumble over problems that were solved years (or even decades ago) in preceding platforms. I leave it as an exercise for the reader to decide if the "reinvent from scratch" critique is more deserved by Docker, Go, or Linux.

That being said, I'm pretty sure even the broken Setuid behavior described there would be good enough to sandbox a thread or child proc that was just handling buffered I/O into and out of the xz binary.

It's not a bug, strictly speaking; it's just a feature that's really easy to misunderstand. The `syscall` package in Go tends to be logic-less wrappers around the raw syscalls, and that's what happens here.

Linux actually maintains the uid/euid/suid/gid/egid/sgid/etc. fields per OS thread (which are actually processes, just with a bit of shared memory). The raw syscalls only change the fields on a single task.

Glibc is where the logic happens to propagate that setting to all threads, by setting up signal handlers and immediately triggering a signal, IIRC.

You can get the useful behaviour by using cgo and importing setresuid from unistd.h.

What go should probably do here is:

1) Add os.Set{res,re,}{u,g}id, which implements the logic from glibc.

2) Remove syscall.Set{res,re,}{u,g}id. Anyone that wants that behaviour can use syscall.Syscall6 and syscall.SYS_SETUID or whatever. At least, they could add some really loud godoc to those methods.

The main problem with that is that `os` tries to have a cross-platform API, and (for example) BSD has no saved user or group IDs and therefore no setres{u,g}id. I suspect Windows and Plan9 are even weirder.

> It's not a bug, strictly speaking; it's just a feature that's really easy to misunderstand.

It is a bug on Linux -- strictly speaking. Did you see how it is patched? They removed setuid from linux: https://codereview.appspot.com/106170043

> "That these functions should made to fail rather than succeed in their broken state."

Wow, nice bug.

I like a lot of things about Go, but I wish they didn't invent their own mini operating system in the runtime on top of Linux/POSIX. This bug seems like a good example of why that's a leaky abstraction.

I think it would be interesting to explore a coroutines + threads + channels design space for a concurrent language. Basically like Go, except without the green thread implementation.

For CPU bound work, you can use threads. For I/O bound work, use coroutines. And then they all compose together with channels. The implementation would be a lot simpler because it works with the OS rather than trying to paper over it.