As it is already painfully known, modifying the process environment in a multi-threaded application during runtime is asking for trouble. However, not always we can control who accesses that.
In this situation, we rely on some 3rd party libraries to which we only have binary distribution (.so files). Unfortunately, they tend to abuse the setenv() function which sporadically causes our logger to crash during a strftime() call. Sometimes, it may crash once in a few days; sometimes it can crash several times per day; but the stack trace always ends up in getenv(), which as you can imagine is not really pleasant for users.
What came to my mind as a potential fix is to hook setenv() and getenv() functions and use a global mutex before calling the original implementation. I'm looking for a more future-proof method that does not contain assembly.
On Windows, I would easily do that by using Microsoft Detours, but I'm not aware of any easy method regarding the *nix world.
In general, I would like to have a working solution for Android C library(Bionic), Apple Clang with their C library and Linux with glibc. Android is my greatest priority as the crashes there are many times more often compared to any other platform. Also, to mention that LD_PRELOAD is not an option; it should be fully C code(of course platform specific) solution from the main executable which I can execute before starting any complex routines.
P.S. I know with such kind of hooks I cannot protect from a direct usage of the global environ variable, but I believe it is not directly used by the 3rd parties.
UPDATE: I want to explicitly state that our codebase does not modify the environment. No usages of setenv/putenv etc. The problem is coming from the 3rd parties which I've mentioned.
“Hooking” functions as you ask is called interposing. Since you ask about multiple platforms, I am not going to speak to that here except to say that searching for “[c] interposition”, “[c] interposing”, and “[c] interpose” will show you questions about that. (There are tags for this, including interposing, function-interposition, and library-interposition, but they seem little used, so I expect the preceding searches will be more productive.)
When you have interposition working, interpose setenv:
setenv, passing it the unchanged parameters.And interpose strftime:
getenv to get TZ, saving it in a temporary copy.setenv to set TZ from the thread-local value. (If the thread-local value is a null pointer, use the original value of TZ from program startup, saved at program startup, or whatever value you desire.)strftime, passing it the unchanged parameters.setenv to restore TZ from the saved value.If your problem is just that the libraries are calling setenv to set names other than TZ and that sometimes causes strftime to crash, you can remove the TZ manipulations from above and reduce each interposing routine to just lock, call the underlying routine, and unlock.
If the library strftime calls setenv and, due to the interposition method, gets your interposing setenv, you will have a thread trying to acquire a lock it already has. You will want to detect that in your setenv and just pass the call through to the underlying setenv without taking the lock and without releasing it afterward.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With