News for hackers. Stuff that matters.
Re:Linux Namespaces and Go Don't Mix by pcwalton on Friday June 02, 2017 @12:40PM (#14471243)
No, it's a lot messier than C. In a regular C program (not using any special libraries for M:N threading) you wouldn't have to spawn an entirely separate process.

This issue is one of the downsides of Go's M:N scheduling. The OS is simply not aware of what the Go runtime is doing, and as a result you get impedance mismatches like this.

It "raises a few eyebrows" because M:N scheduling is unpopular outside of Go and Erlang. It was tried early on in the Linux world and abandoned precisely because of issues like this. Go has repopularized M:N lately, and it's proof that such a system can work for lots of apps, but the downsides of that decision are every bit as real today as they were in the early days of NPTL.

Re:Linux Namespaces and Go Don't Mix by m0th87 on Friday June 02, 2017 @01:00PM (#14471458)
This makes sense, and I know it's why Rust abandoned green threading. But I can't help but worry that the focus on async I/O in Rust as a way of avoiding this issue is going to bring the language down a path that isn't as ergonomically pleasant as Go or Erlang's M:N threading for things like highly concurrent web services. Do you share this concern, or do you think Rust can achieve the same level of ergonomics without the impedance mismatch?
Re:Linux Namespaces and Go Don't Mix by pcwalton on Friday June 02, 2017 @03:36PM (#14472722)
It's harder for us, but I think we can get to an ergonomic solution with async/await.

It's not as easy as threads (M:N vs 1:1 is a red herring as far as this is concerned), but there's no free lunch.

Re:Linux Namespaces and Go Don't Mix by jpgvm on Friday June 02, 2017 @10:49PM (#14474734)
I hope the Rust team takes a look at Kotlins coroutines (and all the stuff they managed to build on top of them). I think Kotlin is a good example because it is built ontop of a 1:1 runtime and uses very elegant primitives.

I was pretty disappointed Rust didn't ship with async/await and some form of lightweight thread but it's understandable.

Re:Linux Namespaces and Go Don't Mix by pencilhappen on Friday June 02, 2017 @07:52PM (#14474075)
async/await still has the fundamental problem of composability that all the other attempts at sugaring around an event loop have (aka the "functions have colors" problem [1]). It sucks for collaboration, which is one of the most important things for modern software development.

1. http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...

Re:Linux Namespaces and Go Don't Mix by pcwalton on Saturday June 03, 2017 @07:14PM (#14479016)
I don't like that article, because (1) it claims Go is doing something new, while Go is really just threads with a particularly idiosyncratic implementation; (2) it ignores the fact that you can convert async to sync by just blocking and sync to async by proxying out to a thread pool. The "what color is your function" problem really isn't a big deal.
Re:Linux Namespaces and Go Don't Mix by pencilhappen on Wednesday June 07, 2017 @03:49PM (#14509358)
A thread pool is far from "ergonomic", it's accidental complexity with numerous gotchas.
Re:Linux Namespaces and Go Don't Mix by geodel on Friday June 02, 2017 @02:08PM (#14472082)
You can see for yourself [1]. Seems many users are not following the patterns of Async IO in Rust.

1. https://www.reddit.com/r/rust/comments/6enj5d/what_does_rust...

Re:Linux Namespaces and Go Don't Mix by pcwalton on Friday June 02, 2017 @03:37PM (#14472728)
1:1 can be made compatible with async I/O with a thread pool, though.
Re:Linux Namespaces and Go Don't Mix by steveklabnik on Friday June 02, 2017 @02:46PM (#14472372)
There's a prototype of async/await that can help a lot with that.
Re:Linux Namespaces and Go Don't Mix by SwellJoe on Friday June 02, 2017 @12:52PM (#14471357)
Historically, much (I would probably argue most) C concurrency has been implemented with fork. Certainly not all, and there are many ways to handle it in C...but fork is really common, and I don't think it's considered all that big of a deal to do so. It is idiomatic (at least historically and across maybe billions of lines of C code), and not much harder to reason about than many kinds of thread implementation in C; in fact, it's easier to reason about fork than something like POSIX threads, IMHO.

But, are you saying that to switch to a namespace in Go one must fork, whereas one wouldn't need to fork in C unless you need to switch namespace and you need concurrency (because you can determine when things will happen with precision in C)? I don't know. Again, this is beyond my understanding of Go right now.

I may just not be understanding the implications of this. The code to deal with it looks reasonable enough to me; it's a smallish function, easily isolated. I think the author did a great job explaining the problem, the troubleshooting, and the solution. I just didn't see the problem as being all that damning of Go...but, that may be a reflection of my shallow understanding of the problem, or of the implications of the cost spawning a new process (to me, I always think back to the old adage "fork is cheap on Linux").

Re:Linux Namespaces and Go Don't Mix by cyphar on Friday June 02, 2017 @01:08PM (#14471536)
> C concurrency has been implemented with fork.

Arguably it has been implemented with clone(2) which has flags.

> in fact, it's easier to reason about fork than something like POSIX threads, IMHO.

Not if you call fork() in a multi-threaded program. That ends _exceptionally_ badly (let's just say there's a reason Go doesn't expose syscall.Fork and it has to do with horrific deadlocks).

> I just didn't see the problem as being all that damning of Go.

The problem is more subtle, and it comes down to maintainability and understandably. If you ever decide to read the runc codebase, I apologise. One of the reasons the codebase is so scattered is because of these sorts of hacks where you have to work around issues in the Go runtime (because it doesn't give you enough control). In the article, whenever you read the small function you have to keep in mind that it's actually spawning a subprocess (which then means you have to think about what namespaces had the process joined and so on). Go is an okay language, but it simply wasn't designed for stuff this low-level. We would be much better served with Rust in my opinion.

> But, are you saying that to switch to a namespace in Go one must fork, whereas one wouldn't need to fork in C unless you need to switch namespace and you need concurrency

In C you don't need to fork to switch namespaces, you just call setns(...). For the PID namespace you need to fork, but that's just a quirk of the interface.

In Go, you theoretically don't need to fork either (syscall.Setns is available). However, there is no real way to safely use it. First of all, the namespace interfaces in Linux are quite fragile when it comes to multi-threaded processes, but combine that with a runtime that will switch you between OS threads at random. And while the documentation on runtime.GOMAXPROC and runtime.LockOSThread might trick you into believing it's possible to stop the Go runtime from doing a clone(CLONE_THREAD|CLONE_PARENT), you can't.

[dead]
Re:Linux Namespaces and Go Don't Mix by klodolph on Friday June 02, 2017 @04:37PM (#14473113)
> There is some prep for ugly contingencies(pthread_atfork) but it usually just works.

Actually, that's not true. Perhaps it's theoretically possible, in some cases, to make your threaded code work with fork with the appropriate pthread_atfork() handlers, but in general, it's a total mess. Thread A has a lock, thread B calls fork(), thread B tries to acquire the lock. The lock is held, but thread A is not running in the child. That much should be obvious.

The usual fix is to write a pthread_atfork() handler for every lock in your program. Before forking, it locks all of your mutexes. After forking, in both the child and parent, it unlocks them. You can see the holes in this. One simple hole is that fork() is async signal safe but pthread_mutex_lock() is not, but most people won't fork() in a signal anyway. The big problem is that you now need to make all of your locks recursive and figure out, globally, what order to lock them in. This means registering your atfork() handlers in exactly the right order, something which is more difficult than it sounds. Another problem is the performance implications of acquiring all the locks in your program.

Namespaces + threads are fine, the only problem here is namespaces + M:N threads + no method for pinning a green thread to an OS thread, like forkOS in Haskell.

Re:Linux Namespaces and Go Don't Mix by the8472 on Saturday June 03, 2017 @01:28AM (#14475136)
> Namespaces + threads are fine, the only problem here is namespaces + M:N threads + no method for pinning a green thread to an OS thread, like forkOS in Haskell.

Not quite. unshare(CLONE_NEWUSER) and setns(fd, CLONE_NEWNS) require your process to be single-threaded. So precise control over threading is needed.

Re:Linux Namespaces and Go Don't Mix by cyphar on Saturday June 03, 2017 @04:40AM (#14475545)
On paper, all code between a fork and exec (in a multithreaded process) must be async-signal safe as well. I'm not sure if this is also true for the callbacks with pthread_atfork (I've personally never used it), but it's something to keep in mind.
Re:Linux Namespaces and Go Don't Mix by klodolph on Sunday June 04, 2017 @09:51PM (#14484813)
There’s a defect report somewhere about this exact topic—there’s no requirement that pthread_atfork handlers are async-signal-safe, but fork is marked as async-signal-safe. The question is whether the documentation should be fixed, whether atfork handlers should just not run if fork is called in a signal handler, etc. IMO if someone’s forking in a signal handler in a multithreaded program nothing good is going to happen anyway.
Re:Linux Namespaces and Go Don't Mix by jankedeen on Friday June 02, 2017 @05:51PM (#14473577)
*...it's a total mess...theoretically it's possible....speculation on standard behavior based on platform behavior...namespace(x)<-threads->chosen implementation detail...functional language reference... -- Translation: Design is broken
Re:Linux Namespaces and Go Don't Mix by klodolph on Friday June 02, 2017 @06:54PM (#14473861)
Well, then allow me to cite the standard for you.

> If a multi-threaded process calls fork() ... the child process may only execute async-signal-safe operations until such time as one of the exec functions is called.

IEEE Std 1003.1-2008, 2016 Edition

http://pubs.opengroup.org/onlinepubs/9699919799/

I don't know about "design is broken". I personally think the fork()/exec() model is a clumsy way to create a new process, but that's a matter of taste.

Re:Linux Namespaces and Go Don't Mix by steveklabnik on Friday June 02, 2017 @05:25PM (#14473433)
> Oh, btw: Fuck RUST. For once and for all fuck this tyranny of coercion by potential IP holders. It is a shit language.

... what? I am confused as to what you're saying here.

[dead]
Re:Linux Namespaces and Go Don't Mix by cyphar on Saturday June 03, 2017 @04:47AM (#14475558)
> Rust is painful to write, painful to learn and makes you ask yourself:

It's painful to write because writing software that is memory safe is not easy. Rust makes it insanely easy in comparison to the "old way" of doing it (hacking together a C program then having to patch security holes every release, never being certain that your code is actually memory safe).

> Can I not write a well designed and correct program in C? Of course I can.

Of course you can. All it takes is a separate formal proofing process, countless hours of static analysis and many more months of fixing security bugs. Good thing that's so much simpler than Rust's static analysis which runs on every compilation and mathematically proves that its memory model was not violated.

I think if we have learned one thing in the history of C programs, it's that this sentiment that "a sufficiently smart programmer can write safe C code" is quite harmful. While technically true, I don't think I've ever met such an individual and I doubt one exists.

Re:Linux Namespaces and Go Don't Mix by je42 on Saturday June 03, 2017 @12:58PM (#14477240)
also, one key aspect is. There are may be individuals out there that can do this.

however, usually code is written by multiple people. (and i would also count your future self as another person).

that collaboration on the same code base requires automatic tests, otherwise the next person will break the code in non-obvious ways.

Re:Linux Namespaces and Go Don't Mix by concede_pluto on Friday June 02, 2017 @11:29PM (#14474865)
> Can I not write a well designed and correct program in C?

We have decades of coredumps and exploits to convince us that nobody can. How many well-known apps can you name that have never blown up randomly? I can't think of a single one. How much more failure until it's reasonable to think that C requires an inhuman level of perfection and almost any alternative would be an improvement?

I hear good things about seL4 but that wasn't written so much as translated from Haskell during a formal process that makes Rust look like finger painting.

Re:Linux Namespaces and Go Don't Mix by cyphar on Saturday June 03, 2017 @02:52AM (#14475344)
> I hear good things about seL4 but that wasn't written so much as translated from Haskell during a formal process that makes Rust look like finger painting.

It's actually more fun than that. They have two implementations of seL4, and they use formal proofs to show that the Haskell model is identical to the C implementation. How much fun is that! /s

Re:Linux Namespaces and Go Don't Mix by catern on Friday June 02, 2017 @11:57PM (#14474932)
GNU ls has never segfaulted or otherwise blown up for me. :)
Re:Linux Namespaces and Go Don't Mix by legulere on Saturday June 03, 2017 @01:27AM (#14475134)
It has segfaulted for other people though: https://lists.gnu.org/archive/html/bug-coreutils/2006-11/msg...
Re:Linux Namespaces and Go Don't Mix by laumars on Saturday June 03, 2017 @05:45AM (#14475646)
GNU Bash segfaults for me whenever I type ~[tab]. Thankfully this only happens on the small few systems I had to manually patch after the epic Bash vulnerability a couple of years ago - older internal systems that are still in use for historic reasons rather than regularly relied upon in production. So it's never been annoying enough to fix.
Re:Linux Namespaces and Go Don't Mix by jankedeen on Monday June 05, 2017 @01:10PM (#14489263)
It will. No program is perfect.

Rust and Go advocates will have you think that they have conquered security via the memory mgmt and API front but there is still the off chance they haven't..or that the SA (what is left of that maligned profession outside playbooks and the devops marketing you read here and online) has allowed you a chance for glory.

Re:Linux Namespaces and Go Don't Mix by jankedeen on Monday June 05, 2017 @01:01PM (#14489172)
There are basically two rules to a well written C program (if I am now allowed to speak despite the public outcry).

1. Do not trust user input. This is a cardinal rule in whatever source. If the rule were followed vigorously in every case there would be 90% less exposure. When you take user input, filter. 2. Learn the standard and stick to it.

Finally #3 (unix) Write an application to do a certain thing well.

Re:Linux Namespaces and Go Don't Mix by concede_pluto on Monday June 05, 2017 @02:08PM (#14489814)
(vouched)

On #1, having just stumbled across a deserializer that can be commanded to allocate a 2^63 byte buffer, I agree 110%.

On #2, the problem is that the standard says things like "walking off the end of an array is undefined behavior" and "use after free is undefined behavior" yet we don't seem to have any programmers who can be trusted to reliably avoid these problems with zero runtime checking.

Re:Linux Namespaces and Go Don't Mix by eropple on Friday June 02, 2017 @11:49PM (#14474917)
I'm downvoting you for being profoundly shitty and mean, not because I disagree with you.

I do disagree with you, as it happens, but I'd downvote this sort of thing if I agreed too. There's a difference between bluntness being read as rudeness and what you're doing. Stop.

Re:Linux Namespaces and Go Don't Mix by solidsnack9000 on Saturday June 03, 2017 @12:21AM (#14474996)
> I can't understand anyone who would compare Rust against C in a positive way without possibly profiting from it.

A year or so ago I wrote a Postgres extension in Rust. It was a real delight to have a package manager and to only have to worry about allocations within Postgres.

But whenever I want to code a big hairball without libraries or namespaces and use autoconf and all that great stuff, I still stick with C.

Re:Linux Namespaces and Go Don't Mix by steveklabnik on Friday June 02, 2017 @06:30PM (#14473768)
> large down vote... community

Please report this to the Hacker News mods, if you have evidence. It's against the rules, and they check this kind of thing. Nobody should be doing it.

> IP Holders, well this is speculative.

All of Rust's stuff is MIT/Apache2 licensed. There is a trademark on the name and logo, held by Mozilla, but other than that, there's none.

Re:Linux Namespaces and Go Don't Mix by inimino on Friday June 02, 2017 @09:46PM (#14474502)
At least in this thread, you're getting downvoted because you are being exceptionally rude, not because of any substantive criticism of Rust.
Re:Linux Namespaces and Go Don't Mix by legulere on Saturday June 03, 2017 @01:32AM (#14475143)
My first thing I wrote in Rust was porting a trivial assignment for a security course in University from C to Rust. It turned out I had an off-by-one buffer overflow that Rust caught.

This clearly showed me the advantage of Rust over C.

Re:Linux Namespaces and Go Don't Mix by pyre on Friday June 02, 2017 @01:58PM (#14472010)
> But, are you saying that to switch to a namespace in Go one must fork

The article is stating that to control the namespace that the threads execute in, that a separate process must be spawned so that the entire process can be forced into the correct namespace. Otherwise, the runtime can spawn new threads as it sees fit and you don't have control over which namespace they are in.

It might be possible to work around this from within the same process if it were possible to force the runtime to not spawn new threads in particular cases. If you could control when the runtime was allowed to spawn new threads, then you could organize the program / threads in a way that would keep the correct code operating in the correct namespace. Unfortunately, you can't.

Disclaimer: I don't know Go, but this is my understanding from reading the article.

Re:Linux Namespaces and Go Don't Mix by dullgiulio on Friday June 02, 2017 @01:10PM (#14471559)
I mostly agree. Of course, for as cheap as a fork can be (and in Linux it's very cheap), it's not as cheap as a systemcall.

When changing namespace happens often in a hot path, forking might not be fast enough.

I disagree with Walton that the blame is on the N:M threading. It's really on the Linux kernel that has never made a clean distinction between threads and processes, both in kernel and in the APIs.

Re:Linux Namespaces and Go Don't Mix by YZF on Saturday June 03, 2017 @01:17AM (#14475113)
I agree. Bestowing certain permissions/properties on a thread doesn't make sense. The thread shouldn't "enter the namespace", an API should retrieve a handle to that namespace that should then be usable from any thread in the process via some appropriate calls.
Re:Linux Namespaces and Go Don't Mix by the8472 on Saturday June 03, 2017 @01:31AM (#14475141)
On the other hand having individual threads entering namespaces means your broker process does not have to constantly switch namespaces, it can use shared memory between differently privileged threads to do the brokering.
Re:Linux Namespaces and Go Don't Mix by YZF on Saturday June 03, 2017 @02:09AM (#14475242)
I know nothing of the implementation here but presumably these namespaces can exist side by side in a way that doesn't require any "switching". If switching is expensive that would make context switching between a thread in one namespace and a thread in another namespace just as expensive?

If you have one thread in one namespace and another in another you now have to worry about what you can do in the context of a callback. This asymmetry just makes any multi-threaded program more complicated than it needs to be (and already is).

Re:Linux Namespaces and Go Don't Mix by the8472 on Saturday June 03, 2017 @02:29AM (#14475289)
Switching is a system call (setns), in principle shared-memory IPC does not involve context switches, just lock-free data structures. I'm not sure how common this is in practice since shared memory also has some downsides if you're doing this for security.

But there also are non-security applications of namespaces.

And it's not like namespaces are the only per-thread thing in linux. Capabilities, uid and signal handlers come to mind.

Re:Linux Namespaces and Go Don't Mix by aidenn0 on Friday June 02, 2017 @01:43PM (#14471897)
Separate threads for IO was really common in C and C++ in the 90s.

In addition fork() style concurrency was essentially nonexistent outside of Unix.

Re:Linux Namespaces and Go Don't Mix by di4na on Friday June 02, 2017 @04:24PM (#14473023)
Just saying, but Erlang do not target that type of "System Programming" and the answer to that problem in Erlang would probably work through totally different way to do it. This namespace thing would not be a problem.

This is not a problem of M:N. This is a problem of Go being badly designed. Not new.

Re:Linux Namespaces and Go Don't Mix by dozzie on Friday June 02, 2017 @06:23PM (#14473739)
More like reification of the very old problem from the times when we got threading API, with thread safety of functions that alter process-wide state (umask(), chdir(), sleep()), except now it's calls that alter some thread-specific thing.
Re:Linux Namespaces and Go Don't Mix by wbl on Friday June 02, 2017 @10:58PM (#14474772)
It's a problem with Linux not permitting inspection of interfaces through a namespace. If I have a chroot I can look in from outside easily. Apparently not so with container interfaces.
Re:Linux Namespaces and Go Don't Mix by _ph_ on Friday June 02, 2017 @02:04PM (#14472053)
As far as I know, Solaris threads are actually M:N threads on the system level. M:N threads are extremely efficient. A "native" thread is a heavyweight construct - e.g. by the stack space allocation. So this puts a limit on how many threads you can spawn in a program. For many tasks, this would force you to roll out your own M:N mapping system yourself. Doing it on the language level usually yields the better results.

Due to the implicit M:N mapping, Go goroutines are extremely cheap. This allows you to spawn as many, as your algorithm naturally requires. The Go runtime will automatically map them to native threads - typically one per avaliable CPU. As a consequence, a Go program that heavily uses goroutines has a pretty clean code and scales without much overhead across a large variation of number of CPU cores.

Re:Linux Namespaces and Go Don't Mix by dap on Friday June 02, 2017 @02:53PM (#14472426)
> As far as I know, Solaris threads are actually M:N threads on the system level.

Not for a long time, for all the reasons the parent mentioned (and then some).

Re:Linux Namespaces and Go Don't Mix by pjmlp on Saturday June 03, 2017 @08:57AM (#14476184)
Only until Solaris 8, Solaris 9 dropped support for the LWP model.
Re:Linux Namespaces and Go Don't Mix by pjmlp on Saturday June 03, 2017 @08:49AM (#14476159)
Also in Solaris, it was tried and abandoned.

Although they have their own set of issues, Windows fibers are also barely used.