diff mbox series

[v2,07/13] stdlib: Add internal __getenvarray function

Message ID d1036d65c3f18dbe2e0a78cdec0bdee449e7c7a9.1722193092.git.fweimer@redhat.com
State New
Headers show
Series getenv/environ thread safety | expand

Commit Message

Florian Weimer July 28, 2024, 7:02 p.m. UTC
---
 stdlib/Makefile      |  1 +
 stdlib/getenvarray.c | 64 ++++++++++++++++++++++++++++++++++++++++++++
 stdlib/setenv.h      |  7 +++++
 3 files changed, 72 insertions(+)
 create mode 100644 stdlib/getenvarray.c
diff mbox series

Patch

diff --git a/stdlib/Makefile b/stdlib/Makefile
index 697b547540..e1f1848439 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -79,6 +79,7 @@  routines := \
   getcontext \
   getentropy \
   getenv \
+  getenvarray \
   getrandom \
   getsubopt \
   jrand48 \
diff --git a/stdlib/getenvarray.c b/stdlib/getenvarray.c
new file mode 100644
index 0000000000..cac308fb32
--- /dev/null
+++ b/stdlib/getenvarray.c
@@ -0,0 +1,64 @@ 
+/* Obtain a consistent copy of the environ array.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <atomic.h>
+#include <setenv.h>
+#include <unistd.h>
+
+size_t
+__getenvarray (char **result, size_t length)
+{
+  while (true)
+    {
+      /* The snapshotting approach follows getenv.  Duplicates are
+         filtered out explicitly.  */
+      environ_counter start_counter = atomic_load_acquire (&__environ_counter);
+      char **start_environ = atomic_load_relaxed (&__environ);
+      if (start_environ == NULL)
+        return 0;
+
+      const char *previous = NULL;
+      size_t count = 0;
+      for (char *const *ep = start_environ; ; ++ep)
+        {
+          char *entry = atomic_load_relaxed (ep);
+          if (entry == NULL)
+            break;
+          if (entry == previous)
+            /* Filter out a duplicate created by concurrent unsetenv.  */
+            continue;
+          if (count < length)
+            result[count] = entry;
+          ++count;
+          previous = entry;
+        }
+
+      atomic_thread_fence_acquire ();
+
+      if (atomic_load_acquire (&__environ_counter) != start_counter)
+        /* Retry if there has been more than one write to the array
+           from within unsetenv.  Writes in setenv/putenv do not
+           matter because they do not change the key, so they cannot
+           introduce duplicates.  */
+        continue;
+
+      if (count < length)
+        result[count] = NULL;
+      return count;
+    }
+}
diff --git a/stdlib/setenv.h b/stdlib/setenv.h
index 3919943f87..69fa9367ed 100644
--- a/stdlib/setenv.h
+++ b/stdlib/setenv.h
@@ -47,6 +47,13 @@  __environ_is_from_array_list (char **ep)
   return eal != NULL && &eal->array[0] == ep;
 }
 
+/* Make a copy of the environ array.  Writes up to LENGTH pointers to
+   *RESULT, including a terminating null pointer if there is room.
+   *Returns the total number of non-null elements in the environ
+   *array.  This number can be larger than LENGTH.  */
+size_t __getenvarray (char **result, size_t length)
+  __attr_access ((__write_only__, 1, 2)) attribute_hidden;
+
 /* Counter for detecting concurrent modification in unsetenv.
    Ideally, this should be a 64-bit counter that cannot wrap around,
    but given that counter wrapround is probably impossible to hit