diff options
author | Karl Williamson <khw@cpan.org> | 2020-03-06 14:18:45 -0700 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2020-03-11 09:52:12 -0600 |
commit | 24f3e849b5ce9f3bf6b6be5d3e730562e927aa79 (patch) | |
tree | 2aeee9a5124aea2f9617971dc2f3421fd8a0e858 /inline.h | |
parent | 2bc5f86adf5f1c0feb76d83e1a627e5649e6beab (diff) | |
download | perl-24f3e849b5ce9f3bf6b6be5d3e730562e927aa79.tar.gz |
Add thread safety to some environment accesses
The previous commit added a mutex specifically for protecting against
simultaneous accesses of the environment. This commit changes the
normal getenv, putenv, and clearenv functions to use it, to avoid races.
This makes the code simpler in places where we've gotten burned and
added stuff to avoid races. Other places where we haven't known we were
getting burned could have existed until now. Now that comes
automatically, and we can remove the special cases we earlier stumbled
over.
getenv() returns a pointer to static memory, which can be overwritten at
any moment from another thread, or even another getenv from the same
thread. This commit changes the accesses to be under control of a
mutex, and in the case of getenv, a mortalized copy is created so that
there is no possible race.
Diffstat (limited to 'inline.h')
-rw-r--r-- | inline.h | 53 |
1 files changed, 53 insertions, 0 deletions
@@ -2586,6 +2586,59 @@ S_my_memrchr(const char * s, const char c, const STRLEN len) #endif +PERL_STATIC_INLINE char * +Perl_mortal_getenv(const char * str) +{ + /* This implements a (mostly) thread-safe, sequential-call-safe getenv(). + * + * It's (mostly) thread-safe because it uses a mutex to prevent + * simultaneous access from other threads that use the same mutex, and + * makes a copy of the result before releasing that mutex. All of the Perl + * core uses that mutex, but, like all mutexes, everything has to cooperate + * for it to completely work. It is possible for code from, say XS, to not + * use this mutex, defeating the safety. + * + * On some platforms, getenv() is not sequential-call-safe, because + * subsequent calls destroy the static storage inside the C library + * returned by an earlier call. The result must be copied or completely + * acted upon before a subsequent getenv call. Those calls could come from + * another thread. Again, making a copy while controlling the mutex + * prevents these problems.. + * + * To prevent leaks, the copy is made by creating a new SV containing it, + * mortalizing the SV, and returning the SV's string (the copy). Thus this + * is a drop-in replacement for getenv(). + * + * A complication is that this can be called during phases where the + * mortalization process isn't available. These are in interpreter + * destruction or early in construction. khw believes that at these times + * there shouldn't be anything else going on, so plain getenv is safe AS + * LONG AS the caller acts on the return before calling it again. */ + + char * ret; + dTHX; + + PERL_ARGS_ASSERT_MORTAL_GETENV; + + /* Can't mortalize without stacks. khw believes that no other threads + * should be running, so no need to lock things, and this may be during a + * phase when locking isn't even available */ + if (UNLIKELY(PL_scopestack_ix == 0)) { + return getenv(str); + } + + ENV_LOCK; + + ret = getenv(str); + + if (ret != NULL) { + ret = SvPVX(sv_2mortal(newSVpv(ret, 0))); + } + + ENV_UNLOCK; + return ret; +} + /* * ex: set ts=8 sts=4 sw=4 et: */ |