TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Don't setenv in multi-threaded code on glibc

150 pointsby r4umover 8 years ago

15 comments

roblablaover 8 years ago
My first thought when reading this is &quot;what about rust&quot; ? Rust uses the system libc to support most of it&#x27;s standard library, so could it have the same problem ?<p>A quick look at the std::env::set_env docs [0] tells us that Rust is aware the underlying implementation are inherently thread-unsafe, and looking at its implementation [1] tells us that rust uses a global lock for all access to the environment.<p>So at least, it will avoid segfaulting your programs.<p>Good job rust :D<p>EDIT: However, I guess if you fork() (which is not something you can do with the standard rust library AFAIK, so requires unsafe), you may get the first problem of having a deadlock.<p>[0]: <a href="https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;env&#x2F;fn.set_var.html" rel="nofollow">https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;env&#x2F;fn.set_var.html</a><p>[1]: <a href="https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;51d29343c04a27570a8ff8282611007d6e6408de&#x2F;src&#x2F;libstd&#x2F;sys&#x2F;unix&#x2F;os.rs#L415" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;51d29343c04a27570a8ff...</a>
评论 #13529307 未加载
评论 #13529339 未加载
评论 #13529837 未加载
评论 #13530485 未加载
评论 #13534512 未加载
quotemstrover 8 years ago
Windows has sane environment-retrieval functions: <a href="https:&#x2F;&#x2F;msdn.microsoft.com&#x2F;en-us&#x2F;library&#x2F;windows&#x2F;desktop&#x2F;ms683188(v=vs.85).aspx" rel="nofollow">https:&#x2F;&#x2F;msdn.microsoft.com&#x2F;en-us&#x2F;library&#x2F;windows&#x2F;desktop&#x2F;ms6...</a><p>Unlike getenv, GetEnvironmentVariable copies a variable&#x27;s value into a <i>caller provided buffer</i>, addressing the use-after-free problem inherent in getenv&#x27;s interface.<p>Wouldn&#x27;t it be nice if glibc got something like a getenv2 with a similar interface? Why should people have to trip over weird corner cases in POSIX over and over and over again? Why are we afraid to add new APIs that make some damn sense?
评论 #13529348 未加载
评论 #13529604 未加载
评论 #13539930 未加载
评论 #13530819 未加载
评论 #13534174 未加载
评论 #13531020 未加载
ChrisFosterover 8 years ago
I&#x27;ve had fun debugging this very problem in the past, as the root cause of a very puzzling intermittent failure.<p>The symptoms were intermittent segfaults in some high level scientific python code running on a large cluster in AWS. Given we had no custom C library extensions, this should be impossible, but there they were; the jobs would fail randomly for one in every several thousand runs, if memory serves.<p>We eventually tracked this to a call to getenv() in the OpenBLAS thread pool per-thread initialization code. This seems innocuous enough by itself, but couple it with some other unrelated python library which happened to call setenv() and you have a nice race condition accessing the internal glibc data structures. Ultimately this ended up with a segfault due to dereferencing some already freed memory.<p>Ah, good times. Here&#x27;s the root cause analysis if anyone wants the gory details :-) <a href="https:&#x2F;&#x2F;github.com&#x2F;xianyi&#x2F;OpenBLAS&#x2F;issues&#x2F;716#issuecomment-164339663" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;xianyi&#x2F;OpenBLAS&#x2F;issues&#x2F;716#issuecomment-1...</a>
noselasdover 8 years ago
Another fun one is &quot;don&#x27;t exit() in multi-threaded code on glibc&quot;.<p>It goes like this:<p>1. Thread A calls FILE f = fopen(); if (f == NULL) {error}<p>2. Thread B calls exit(). exit() flushes and closes all open FILE (it does so it an internal atexit() handler)<p>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
评论 #13533428 未加载
评论 #13534257 未加载
Sharlinover 8 years ago
I guess this is one of those &quot;obvious in retrospect&quot; things, but modifying global state in a multithreaded program is definitely something that should immediately ring alarm bells unless the API is specified to be thread-safe.
nickcwover 8 years ago
I wrote the equivalent code in Go to see what happened<p><a href="https:&#x2F;&#x2F;play.golang.org&#x2F;p&#x2F;E3glBkbAo3" rel="nofollow">https:&#x2F;&#x2F;play.golang.org&#x2F;p&#x2F;E3glBkbAo3</a><p>(You&#x27;ll need to run it locally for the full effect - things on play are limited to a single thread.)<p>It didn&#x27;t crash.<p>I wasn&#x27;t suprised though as Go doesn&#x27;t use the C library and its authors are very careful about multithreaded code.
评论 #13529787 未加载
ameliusover 8 years ago
How is it even defined to setenv something in multi-threaded code?<p>Also, if you want to pass an environment variable to a child process from a specific thread, how do you do that without interfering with other threads that might do the same (alter the same variable to pass it to a different child process)? Would that require an ugly mutex around invoking child processes?
评论 #13529892 未加载
评论 #13531088 未加载
jakeoghover 8 years ago
Does musl have the same prob?
评论 #13529789 未加载
qwertyuiop924over 8 years ago
You know, I&#x27;m starting to think that multithreading may not be the best idea in an environment explicitly not designed for it, and we should be using other approaches, it the latency requirements of the application make that possible.<p>If only we had some sort of shared-nothing concurrency, like a syscall that <i>forks</i> your process and uses copy-on-write to make it efficient. But that&#x27;s just crazy talk.
tropoover 8 years ago
We should just deprecate setenv(). There is no need for it because we have execve().<p>I suppose there is system() on non-POSIX, but then you don&#x27;t have a portable shell anyway nor do you have portable environment variables. You&#x27;re off in 100% non-portable territory at that point, so no point having it in the standards.
jwilkover 8 years ago
If setenv() is not thread-safe, then why is it using locks?
评论 #13529943 未加载
py_throwover 8 years ago
What about Python? Is it also affected?
评论 #13530063 未加载
dendisuhubdyover 8 years ago
grok-machine:glibc dendisuhubdy$ gcc -g -o mtenv metenv.c -lpthread &amp;&amp; .&#x2F;mtenv start<p>no segfault detected
ambrop7over 8 years ago
(ignore this comment)
评论 #13533630 未加载
aangjieover 8 years ago
Funny.. it seems gcc 5.4.0 shipped with ubuntu 16.04 deletes the source file if we try to compile(fails with a linker error) it.