News for hackers. Stuff that matters.
Re:Don't setenv in multi-threaded code on glibc by noselasd on Tuesday January 31, 2017 @10:20AM (#13531237)
Another fun one is "don't exit() in multi-threaded code on glibc".

It goes like this:

1. Thread A calls FILE f = fopen(); if (f == NULL) {error}

2. Thread B calls exit(). exit() flushes and closes all open FILE (it does so it an internal atexit() handler)

3. Thread A determined that the file opened just fine, f != NULL. But Thread B has called fclose() on it, so any use in thread A is a use-after-free if Thread B is a tad slow in actually exiting the process

Re:Don't setenv in multi-threaded code on glibc by quotemstr on Tuesday January 31, 2017 @01:14PM (#13533428)
If this bug actually still happens, it's an implementation problem in glibc, not a fundamental problem with the fopen and fclose interface. Regular locks can guard against this scenario just fine.

Do you have a glibc bug number?

Re:Don't setenv in multi-threaded code on glibc by mzs on Tuesday January 31, 2017 @04:39PM (#13535497)
Not really, here's a different example where locking would not help you with file streams. You expect all streams to be flushed on exit() but I can't think of a unix-like that handles at_exit(_exit); correctly then, correctly in the sense that the streams no longer get flushed. In fact I have used this feature to prevent that flushing on exit() ;)
Re:Don't setenv in multi-threaded code on glibc by quotemstr on Tuesday January 31, 2017 @05:23PM (#13535889)
Your example doesn't result in a deadlock or undefined behavior though. It just results in some at-exit code not being run.

Really, programs shouldn't expect _anything_ to happen on exit. After all, programs can die at any time for any reason anyway. Personally, I'd do away with both atexit and static destructors. Your program needs to be able to handle the power cord being yanked, so it needs to be able to handle sudden death no matter what.

Re:Don't setenv in multi-threaded code on glibc by noselasd on Tuesday January 31, 2017 @02:42PM (#13534454)
Re:Don't setenv in multi-threaded code on glibc by mzs on Tuesday January 31, 2017 @02:24PM (#13534257)
exit() isn't async-safe, but _exit() is, so use _exit() in MT code.

glibc had a misfeature until 2.3 for _exit in MT programs, read the manpage ;)

Re:Don't setenv in multi-threaded code on glibc by noselasd on Tuesday January 31, 2017 @02:54PM (#13534573)
Being async-safe just means you can call it in a signal handler, and is unrelated to being thread safe.

_exit() will cause a similar race condition, but probably with less severe effects. _exit() closes all open file descriptors (not FILE*, like exit() does).

That means while exit() is executing, another thread could perform a file operation on a file descriptor that just was closed by the other thread running exit(). imo. glibc exit() implementation should halt/pause all the other threads before it goes on to mess with shared resources before it terminates the process.

Re:Don't setenv in multi-threaded code on glibc by mzs on Tuesday January 31, 2017 @04:24PM (#13535373)
look here: http://lxr.free-electrons.com/source/kernel/exit.c?v=3.12#L9...

Conceptually all that you described (plus some more, thing SysV IPC and MM) happens in the kernel (although the behaviour of streams is not specified, I can't think of any OS that does more than close FDs) after the OS has made it so every thread in the process can't go forward. I alluded to the misfeature in glibc, that's why you used to have to do raise(9) on linux to work around the oddity. Every other OS just did the right thing (though you sometimes had to build or link your program differently), ie all threads are 'stopped' before any of the things like closing FDs happens.