1 /* Copyright (C) 1992-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #if HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 /* Pacify GCC; see the commentary about VALLEN below.  This is needed
23    at least through GCC 4.9.2.  Pacify GCC for the entire file, as
24    there seems to be no way to pacify GCC selectively, only for the
25    place where it's needed.  Do not use DIAG_IGNORE_NEEDS_COMMENT
26    here, as it's not defined yet.  */
27 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
28 
29 #include <errno.h>
30 #if !_LIBC
31 # if !defined errno && !defined HAVE_ERRNO_DECL
32 extern int errno;
33 # endif
34 # define __set_errno(ev) ((errno) = (ev))
35 #endif
36 
37 #if _LIBC || HAVE_STDLIB_H
38 # include <stdlib.h>
39 #endif
40 #if _LIBC || HAVE_STRING_H
41 # include <string.h>
42 #endif
43 #if _LIBC || HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif
46 
47 #if !_LIBC
48 # define __environ	environ
49 # ifndef HAVE_ENVIRON_DECL
50 extern char **environ;
51 # endif
52 #endif
53 
54 #if _LIBC
55 /* This lock protects against simultaneous modifications of `environ'.  */
56 # include <libc-lock.h>
57 __libc_lock_define_initialized (static, envlock)
58 # define LOCK	__libc_lock_lock (envlock)
59 # define UNLOCK	__libc_lock_unlock (envlock)
60 #else
61 # define LOCK
62 # define UNLOCK
63 #endif
64 
65 /* In the GNU C library we must keep the namespace clean.  */
66 #ifdef _LIBC
67 # define setenv __setenv
68 # define unsetenv __unsetenv
69 # define clearenv __clearenv
70 # define tfind __tfind
71 # define tsearch __tsearch
72 #endif
73 
74 /* In the GNU C library implementation we try to be more clever and
75    allow arbitrarily many changes of the environment given that the used
76    values are from a small set.  Outside glibc this will eat up all
77    memory after a while.  */
78 #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
79 		      && defined __GNUC__)
80 # define USE_TSEARCH	1
81 # include <search.h>
82 
83 /* This is a pointer to the root of the search tree with the known
84    values.  */
85 static void *known_values;
86 
87 # define KNOWN_VALUE(Str) \
88   ({									      \
89     void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp);	      \
90     value != NULL ? *(char **) value : NULL;				      \
91   })
92 # define STORE_VALUE(Str) \
93   tsearch (Str, &known_values, (__compar_fn_t) strcmp)
94 
95 #else
96 # undef USE_TSEARCH
97 
98 # define KNOWN_VALUE(Str) NULL
99 # define STORE_VALUE(Str) do { } while (0)
100 
101 #endif
102 
103 
104 /* If this variable is not a null pointer we allocated the current
105    environment.  */
106 static char **last_environ;
107 
108 
109 /* This function is used by `setenv' and `putenv'.  The difference between
110    the two functions is that for the former must create a new string which
111    is then placed in the environment, while the argument of `putenv'
112    must be used directly.  This is all complicated by the fact that we try
113    to reuse values once generated for a `setenv' call since we can never
114    free the strings.  */
115 int
__add_to_environ(const char * name,const char * value,const char * combined,int replace)116 __add_to_environ (const char *name, const char *value, const char *combined,
117 		  int replace)
118 {
119   char **ep;
120   size_t size;
121 
122   /* Compute lengths before locking, so that the critical section is
123      less of a performance bottleneck.  VALLEN is needed only if
124      COMBINED is null (unfortunately GCC is not smart enough to deduce
125      this; see the #pragma at the start of this file).  Testing
126      COMBINED instead of VALUE causes setenv (..., NULL, ...)  to dump
127      core now instead of corrupting memory later.  */
128   const size_t namelen = strlen (name);
129   size_t vallen;
130   if (combined == NULL)
131     vallen = strlen (value) + 1;
132 
133   LOCK;
134 
135   /* We have to get the pointer now that we have the lock and not earlier
136      since another thread might have created a new environment.  */
137   ep = __environ;
138 
139   size = 0;
140   if (ep != NULL)
141     {
142       for (; *ep != NULL; ++ep)
143 	if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
144 	  break;
145 	else
146 	  ++size;
147     }
148 
149   if (ep == NULL || __builtin_expect (*ep == NULL, 1))
150     {
151       char **new_environ;
152 
153       /* We allocated this space; we can extend it.  Avoid using the raw
154 	 reallocated pointer to avoid GCC -Wuse-after-free.  */
155       uintptr_t ip_last_environ = (uintptr_t)last_environ;
156       new_environ = (char **) realloc (last_environ,
157 				       (size + 2) * sizeof (char *));
158       if (new_environ == NULL)
159 	{
160 	  UNLOCK;
161 	  return -1;
162 	}
163 
164       if ((uintptr_t)__environ != ip_last_environ)
165 	memcpy ((char *) new_environ, (char *) __environ,
166 		size * sizeof (char *));
167 
168       new_environ[size] = NULL;
169       new_environ[size + 1] = NULL;
170       ep = new_environ + size;
171 
172       last_environ = __environ = new_environ;
173     }
174   if (*ep == NULL || replace)
175     {
176       char *np;
177 
178       /* Use the user string if given.  */
179       if (combined != NULL)
180 	np = (char *) combined;
181       else
182 	{
183 	  const size_t varlen = namelen + 1 + vallen;
184 #ifdef USE_TSEARCH
185 	  char *new_value;
186 	  int use_alloca = __libc_use_alloca (varlen);
187 	  if (__builtin_expect (use_alloca, 1))
188 	    new_value = (char *) alloca (varlen);
189 	  else
190 	    {
191 	      new_value = malloc (varlen);
192 	      if (new_value == NULL)
193 		{
194 		  UNLOCK;
195 		  return -1;
196 		}
197 	    }
198 # ifdef _LIBC
199 	  __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
200 		     value, vallen);
201 # else
202 	  memcpy (new_value, name, namelen);
203 	  new_value[namelen] = '=';
204 	  memcpy (&new_value[namelen + 1], value, vallen);
205 # endif
206 
207 	  np = KNOWN_VALUE (new_value);
208 	  if (__glibc_likely (np == NULL))
209 #endif
210 	    {
211 #ifdef USE_TSEARCH
212 	      if (__glibc_unlikely (! use_alloca))
213 		np = new_value;
214 	      else
215 #endif
216 		{
217 		  np = malloc (varlen);
218 		  if (__glibc_unlikely (np == NULL))
219 		    {
220 		      UNLOCK;
221 		      return -1;
222 		    }
223 
224 #ifdef USE_TSEARCH
225 		  memcpy (np, new_value, varlen);
226 #else
227 		  memcpy (np, name, namelen);
228 		  np[namelen] = '=';
229 		  memcpy (&np[namelen + 1], value, vallen);
230 #endif
231 		}
232 	      /* And remember the value.  */
233 	      STORE_VALUE (np);
234 	    }
235 #ifdef USE_TSEARCH
236 	  else
237 	    {
238 	      if (__glibc_unlikely (! use_alloca))
239 		free (new_value);
240 	    }
241 #endif
242 	}
243 
244       *ep = np;
245     }
246 
247   UNLOCK;
248 
249   return 0;
250 }
251 
252 int
setenv(const char * name,const char * value,int replace)253 setenv (const char *name, const char *value, int replace)
254 {
255   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
256     {
257       __set_errno (EINVAL);
258       return -1;
259     }
260 
261   return __add_to_environ (name, value, NULL, replace);
262 }
263 
264 int
unsetenv(const char * name)265 unsetenv (const char *name)
266 {
267   size_t len;
268   char **ep;
269 
270   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
271     {
272       __set_errno (EINVAL);
273       return -1;
274     }
275 
276   len = strlen (name);
277 
278   LOCK;
279 
280   ep = __environ;
281   if (ep != NULL)
282     while (*ep != NULL)
283       {
284 	if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
285 	  {
286 	    /* Found it.  Remove this pointer by moving later ones back.  */
287 	    char **dp = ep;
288 
289 	    do
290 		dp[0] = dp[1];
291 	    while (*dp++);
292 	    /* Continue the loop in case NAME appears again.  */
293 	  }
294 	else
295 	  ++ep;
296       }
297 
298   UNLOCK;
299 
300   return 0;
301 }
302 
303 /* The `clearenv' was planned to be added to POSIX.1 but probably
304    never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
305    for Fortran 77) requires this function.  */
306 int
clearenv(void)307 clearenv (void)
308 {
309   LOCK;
310 
311   if (__environ == last_environ && __environ != NULL)
312     {
313       /* We allocated this environment so we can free it.  */
314       free (__environ);
315       last_environ = NULL;
316     }
317 
318   /* Clear the environment pointer removes the whole environment.  */
319   __environ = NULL;
320 
321   UNLOCK;
322 
323   return 0;
324 }
325 #ifdef _LIBC
libc_freeres_fn(free_mem)326 libc_freeres_fn (free_mem)
327 {
328   /* Remove all traces.  */
329   clearenv ();
330 
331   /* Now remove the search tree.  */
332   __tdestroy (known_values, free);
333   known_values = NULL;
334 }
335 
336 # undef setenv
337 # undef unsetenv
338 # undef clearenv
339 weak_alias (__setenv, setenv)
340 weak_alias (__unsetenv, unsetenv)
341 weak_alias (__clearenv, clearenv)
342 #endif
343