This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add Configure probe for getenv() buffer race
authorKarl Williamson <khw@cpan.org>
Sat, 28 Nov 2020 16:20:46 +0000 (09:20 -0700)
committerKarl Williamson <khw@cpan.org>
Sun, 20 Dec 2020 05:00:29 +0000 (22:00 -0700)
Most implementations do not have a problem with two getenv()'s running
simultaneously in different threads.  But Posix doesn't require such
good behavior.  This adds a simple probe to test the current system.

14 files changed:
Configure
Cross/config.sh-arm-linux
Cross/config.sh-arm-linux-n770
NetWare/config.wc
Porting/config.sh
config_h.SH
configure.com
metaconfig.h
plan9/config_sh.sample
uconfig.h
uconfig.sh
uconfig64.sh
win32/config.gc
win32/config.vc

index 16d6370..90ea4bd 100755 (executable)
--- a/Configure
+++ b/Configure
@@ -514,6 +514,7 @@ d_gai_strerror=''
 d_Gconvert=''
 d_getaddrinfo=''
 d_getcwd=''
+d_getenv_preserves_other_thread=''
 d_getespwnam=''
 d_getfsstat=''
 d_getgrent=''
@@ -14206,6 +14207,86 @@ eval $inlibc
 set getcwd d_getcwd
 eval $inlibc
 
+: check for getenv behavior
+case "$d_getenv_preserves_other_thread" in
+'')
+$echo "Checking to see if getenv() preserves a different thread's results" >&4
+$cat >try.c <<EOCP
+#$i_stdlib I_STDLIB
+#ifdef I_STDLIB
+#  include <stdlib.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#$i_pthread I_PTHREAD
+#ifdef I_PTHREAD
+#  include <pthread.h>
+#endif
+
+void *
+thread_start(void * arg)
+{
+    (void *) getenv("HOME");
+}
+
+int main() {
+    char * main_buffer;
+    char save_main_buffer[1000];
+    pthread_t subthread;
+    pthread_attr_t attr;
+
+    main_buffer = getenv("PATH");
+
+    /* If too large for our generous allowance, return we couldn't figure it
+     * out. */
+    if (strlen(main_buffer) >= sizeof(save_main_buffer)) {
+        exit(2);
+    }
+
+    strcpy(save_main_buffer, main_buffer);
+
+    if (pthread_attr_init(&attr) != 0) {
+        exit(2);
+    }
+
+    if (pthread_create(&subthread, &attr, thread_start, NULL) != 0) {
+        exit(2);
+    }
+
+    if (pthread_join(subthread, NULL) != 0) {
+        exit(2);
+    }
+
+    exit(! strcmp(main_buffer, save_main_buffer) == 0);
+}
+EOCP
+val=
+set try
+if eval $compile_ok; then
+        $run ./try
+        rc=$?
+        case "$rc" in
+            0) echo "getenv() didn't destroy another thread's buffer" >&4
+              val=$define
+               ;;
+            1) echo "getenv() does destroy another thread's buffer" >&4
+              val=$undef
+               ;;
+            *) echo "Couldn't determine if getenv() destroys another thread's return value (code=$rc); assuming it does" >&4
+              val=$undef
+               ;;
+        esac
+else
+    echo "(I can't seem to compile the test program.)" >&4
+    echo "Assuming that your C library's getenv destroys another thread's return value." >&4
+    val=$undef
+fi
+set d_getenv_preserves_other_thread
+eval $setvar
+$rm_try
+;;
+esac
+
 : see if getespwnam exists
 set getespwnam d_getespwnam
 eval $inlibc
@@ -24251,6 +24332,7 @@ d_gdbm_ndbm_h_uses_prototypes='$d_gdbm_ndbm_h_uses_prototypes'
 d_gdbmndbm_h_uses_prototypes='$d_gdbmndbm_h_uses_prototypes'
 d_getaddrinfo='$d_getaddrinfo'
 d_getcwd='$d_getcwd'
+d_getenv_preserves_other_thread='$d_getenv_preserves_other_thread'
 d_getespwnam='$d_getespwnam'
 d_getfsstat='$d_getfsstat'
 d_getgrent='$d_getgrent'
index fa4d036..170da9b 100644 (file)
@@ -248,6 +248,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='define'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='define'
index b49c2f1..668d690 100644 (file)
@@ -247,6 +247,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='define'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='define'
index 5f55e12..26c1755 100644 (file)
@@ -236,6 +236,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='define'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='undef'
index 1401eaa..392decb 100644 (file)
@@ -264,6 +264,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='define'
 d_getcwd='define'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='define'
index 19e1824..dceb480 100755 (executable)
@@ -4575,6 +4575,19 @@ sed <<!GROK!THIS! >$CONFIG_H -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un
 #$d_endservent_r HAS_ENDSERVENT_R      /**/
 #define ENDSERVENT_R_PROTO $endservent_r_proto /**/
 
+/* GETENV_PRESERVES_OTHER_THREAD:
+ *     This symbol, if defined, indicates that the getenv system call doesn't
+ *     zap the static buffer of getenv() in a different thread.
+ *
+ *     The typical getenv() implementation will return a pointer to the proper
+ *     position in **environ.  But some may instead copy them to a static
+ *     buffer in getenv().  If there is a per-thread instance of that buffer,
+ *     or the return points to **environ, then a many-reader/1-writer mutex
+ *     will work; otherwise an exclusive locking mutex is required to prevent
+ *     races.
+ */
+#$d_getenv_preserves_other_thread GETENV_PRESERVES_OTHER_THREAD        /**/
+
 /* HAS_GETGRENT_R:
  *     This symbol, if defined, indicates that the getgrent_r routine
  *     is available to getgrent re-entrantly.
index e7ea8b9..77ce7ae 100644 (file)
@@ -5603,6 +5603,7 @@ $ THEN
 $   vms_cc_type="decc"
 $ ENDIF
 $ d_faststdio="define"
+$ d_getenv_preserves_other_thread="define"
 $ d_locconv="define"
 $ d_mblen="define"
 $ d_mbstowcs="define"
@@ -6398,6 +6399,7 @@ $ WC "d_nextafter='" + d_nextafter + "'"
 $ WC "d_nexttoward='" + d_nexttoward + "'"
 $ WC "d_nice='define'"
 $ WC "d_nl_langinfo='" + d_nl_langinfo + "'"
+$ WC "d_getenv_preserves_other_thread='" + d_getenv_preserves_other_thread + "'"
 $ WC "d_nv_preserves_uv='" + d_nv_preserves_uv + "'"
 $ WC "nv_overflows_integers_at='" + nv_overflows_integers_at + "'"
 $ WC "nv_preserves_uv_bits='" + nv_preserves_uv_bits + "'"
index baba5ea..ae0093a 100644 (file)
@@ -14,4 +14,6 @@
  * they should be removed from here.
  *
  * HAS_WCRTOMB
+ * GETENV_PRESERVES_OTHER_THREAD
+ *
  */
index dfd7c32..691f907 100644 (file)
@@ -248,6 +248,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='define'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='define'
index 91f40df..9df1564 100644 (file)
--- a/uconfig.h
+++ b/uconfig.h
 /*#define HAS_ENDSERVENT_R     / **/
 #define ENDSERVENT_R_PROTO 0   /**/
 
+/* GETENV_PRESERVES_OTHER_THREAD:
+ *     This symbol, if defined, indicates that the getenv system call doesn't
+ *     zap the static buffer of getenv() in a different thread.
+ *
+ *     The typical getenv() implementation will return a pointer to the proper
+ *     position in **environ.  But some may instead copy them to a static
+ *     buffer in getenv().  If there is a per-thread instance of that buffer,
+ *     or the return points to **environ, then a many-reader/1-writer mutex
+ *     will work; otherwise an exclusive locking mutex is required to prevent
+ *     races.
+ */
+#define GETENV_PRESERVES_OTHER_THREAD  /**/
+
 /* HAS_GETGRENT_R:
  *     This symbol, if defined, indicates that the getgrent_r routine
  *     is available to getgrent re-entrantly.
 #endif
 
 /* Generated from:
- * 404722487cbb4753192fd5c9d2e186551220f35fef1347ce39d942abaa90cbf4 config_h.SH
- * 4c3159a6a9875b7811c2a920d7936d5199193afdb163473c313b9531ba2c0648 uconfig.sh
+ * 53ec858c462f9fa2669095834b3d350458c955777a07a0ad7a3a73162ff8ef0e config_h.SH
+ * b53784d20c0f250807f47a3130cdc8e01a92da948e6747af87ebc24f11904722 uconfig.sh
  * ex: set ro: */
index 7747dd6..392070d 100644 (file)
@@ -187,6 +187,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='undef'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='undef'
index f87cb05..6f238fc 100644 (file)
@@ -187,6 +187,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='undef'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='undef'
index 9ffec52..b6e298f 100644 (file)
@@ -235,6 +235,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='define'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='undef'
index 6d6e675..f4625bf 100644 (file)
@@ -235,6 +235,7 @@ d_gdbm_ndbm_h_uses_prototypes='undef'
 d_gdbmndbm_h_uses_prototypes='undef'
 d_getaddrinfo='undef'
 d_getcwd='define'
+d_getenv_preserves_other_thread='define'
 d_getespwnam='undef'
 d_getfsstat='undef'
 d_getgrent='undef'