locale: Fix localedata/sort-test undefined behavior
[glibc.git] / elf / dl-tunables.c
blob1666736bc1fed8d74cc789bb9c8857cd313e0e87
1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2021 Free Software Foundation, Inc.
5 This file is part of the GNU C Library.
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, see
19 <https://www.gnu.org/licenses/>. */
21 /* Mark symbols hidden in static PIE for early self relocation to work. */
22 #if BUILD_PIE_DEFAULT
23 # pragma GCC visibility push(hidden)
24 #endif
25 #include <startup.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <sysdep.h>
31 #include <fcntl.h>
32 #include <ldsodefs.h>
33 #include <array_length.h>
35 #define TUNABLES_INTERNAL 1
36 #include "dl-tunables.h"
38 #include <not-errno.h>
40 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
41 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
42 #endif
44 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
45 static char *
46 tunables_strdup (const char *in)
48 size_t i = 0;
50 while (in[i++] != '\0');
51 char *out = __sbrk (i);
53 /* For most of the tunables code, we ignore user errors. However,
54 this is a system error - and running out of memory at program
55 startup should be reported, so we do. */
56 if (out == (void *)-1)
57 _dl_fatal_printf ("sbrk() failure while processing tunables\n");
59 while (i-- > 0)
60 out[i] = in[i];
62 return out;
64 #endif
66 static char **
67 get_next_env (char **envp, char **name, size_t *namelen, char **val,
68 char ***prev_envp)
70 while (envp != NULL && *envp != NULL)
72 char **prev = envp;
73 char *envline = *envp++;
74 int len = 0;
76 while (envline[len] != '\0' && envline[len] != '=')
77 len++;
79 /* Just the name and no value, go to the next one. */
80 if (envline[len] == '\0')
81 continue;
83 *name = envline;
84 *namelen = len;
85 *val = &envline[len + 1];
86 *prev_envp = prev;
88 return envp;
91 return NULL;
94 static void
95 do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
96 const tunable_num_t *minp,
97 const tunable_num_t *maxp)
99 tunable_num_t val, min, max;
101 if (cur->type.type_code == TUNABLE_TYPE_STRING)
103 cur->val.strval = valp->strval;
104 cur->initialized = true;
105 return;
108 bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
110 val = valp->numval;
111 min = minp != NULL ? *minp : cur->type.min;
112 max = maxp != NULL ? *maxp : cur->type.max;
114 /* We allow only increasingly restrictive bounds. */
115 if (tunable_val_lt (min, cur->type.min, unsigned_cmp))
116 min = cur->type.min;
118 if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
119 max = cur->type.max;
121 /* Skip both bounds if they're inconsistent. */
122 if (tunable_val_gt (min, max, unsigned_cmp))
124 min = cur->type.min;
125 max = cur->type.max;
128 /* Bail out if the bounds are not valid. */
129 if (tunable_val_lt (val, min, unsigned_cmp)
130 || tunable_val_lt (max, val, unsigned_cmp))
131 return;
133 cur->val.numval = val;
134 cur->type.min = min;
135 cur->type.max = max;
136 cur->initialized = true;
139 /* Validate range of the input value and initialize the tunable CUR if it looks
140 good. */
141 static void
142 tunable_initialize (tunable_t *cur, const char *strval)
144 tunable_val_t val;
146 if (cur->type.type_code != TUNABLE_TYPE_STRING)
147 val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
148 else
149 val.strval = strval;
150 do_tunable_update_val (cur, &val, NULL, NULL);
153 void
154 __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
155 tunable_num_t *maxp)
157 tunable_t *cur = &tunable_list[id];
159 do_tunable_update_val (cur, valp, minp, maxp);
162 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
163 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
164 be unsafe for AT_SECURE processes so that it can be used as the new
165 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
166 environment variable string which we use to make NULL terminated values so
167 that we don't have to allocate memory again for it. */
168 static void
169 parse_tunables (char *tunestr, char *valstring)
171 if (tunestr == NULL || *tunestr == '\0')
172 return;
174 char *p = tunestr;
175 size_t off = 0;
177 while (true)
179 char *name = p;
180 size_t len = 0;
182 /* First, find where the name ends. */
183 while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
184 len++;
186 /* If we reach the end of the string before getting a valid name-value
187 pair, bail out. */
188 if (p[len] == '\0')
190 if (__libc_enable_secure)
191 tunestr[off] = '\0';
192 return;
195 /* We did not find a valid name-value pair before encountering the
196 colon. */
197 if (p[len]== ':')
199 p += len + 1;
200 continue;
203 p += len + 1;
205 /* Take the value from the valstring since we need to NULL terminate it. */
206 char *value = &valstring[p - tunestr];
207 len = 0;
209 while (p[len] != ':' && p[len] != '\0')
210 len++;
212 /* Add the tunable if it exists. */
213 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
215 tunable_t *cur = &tunable_list[i];
217 if (tunable_is_name (cur->name, name))
219 /* If we are in a secure context (AT_SECURE) then ignore the
220 tunable unless it is explicitly marked as secure. Tunable
221 values take precedence over their envvar aliases. We write
222 the tunables that are not SXID_ERASE back to TUNESTR, thus
223 dropping all SXID_ERASE tunables and any invalid or
224 unrecognized tunables. */
225 if (__libc_enable_secure)
227 if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
229 if (off > 0)
230 tunestr[off++] = ':';
232 const char *n = cur->name;
234 while (*n != '\0')
235 tunestr[off++] = *n++;
237 tunestr[off++] = '=';
239 for (size_t j = 0; j < len; j++)
240 tunestr[off++] = value[j];
243 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
244 break;
247 value[len] = '\0';
248 tunable_initialize (cur, value);
249 break;
253 if (p[len] != '\0')
254 p += len + 1;
257 #endif
259 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
260 the system administrator has created the /etc/suid-debug file. This is a
261 special case where we want to conditionally enable/disable a tunable even
262 for setuid binaries. We use the special version of access() to avoid
263 setting ERRNO, which is a TLS variable since TLS has not yet been set
264 up. */
265 static __always_inline void
266 maybe_enable_malloc_check (void)
268 tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
269 if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
270 tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
273 /* Initialize the tunables list from the environment. For now we only use the
274 ENV_ALIAS to find values. Later we will also use the tunable names to find
275 values. */
276 void
277 __tunables_init (char **envp)
279 char *envname = NULL;
280 char *envval = NULL;
281 size_t len = 0;
282 char **prev_envp = envp;
284 maybe_enable_malloc_check ();
286 while ((envp = get_next_env (envp, &envname, &len, &envval,
287 &prev_envp)) != NULL)
289 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
290 if (tunable_is_name (GLIBC_TUNABLES, envname))
292 char *new_env = tunables_strdup (envname);
293 if (new_env != NULL)
294 parse_tunables (new_env + len + 1, envval);
295 /* Put in the updated envval. */
296 *prev_envp = new_env;
297 continue;
299 #endif
301 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
303 tunable_t *cur = &tunable_list[i];
305 /* Skip over tunables that have either been set already or should be
306 skipped. */
307 if (cur->initialized || cur->env_alias[0] == '\0')
308 continue;
310 const char *name = cur->env_alias;
312 /* We have a match. Initialize and move on to the next line. */
313 if (tunable_is_name (name, envname))
315 /* For AT_SECURE binaries, we need to check the security settings of
316 the tunable and decide whether we read the value and also whether
317 we erase the value so that child processes don't inherit them in
318 the environment. */
319 if (__libc_enable_secure)
321 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
323 /* Erase the environment variable. */
324 char **ep = prev_envp;
326 while (*ep != NULL)
328 if (tunable_is_name (name, *ep))
330 char **dp = ep;
333 dp[0] = dp[1];
334 while (*dp++);
336 else
337 ++ep;
339 /* Reset the iterator so that we read the environment again
340 from the point we erased. */
341 envp = prev_envp;
344 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
345 continue;
348 tunable_initialize (cur, envval);
349 break;
355 void
356 __tunables_print (void)
358 for (int i = 0; i < array_length (tunable_list); i++)
360 const tunable_t *cur = &tunable_list[i];
361 if (cur->type.type_code == TUNABLE_TYPE_STRING
362 && cur->val.strval == NULL)
363 _dl_printf ("%s:\n", cur->name);
364 else
366 _dl_printf ("%s: ", cur->name);
367 switch (cur->type.type_code)
369 case TUNABLE_TYPE_INT_32:
370 _dl_printf ("%d (min: %d, max: %d)\n",
371 (int) cur->val.numval,
372 (int) cur->type.min,
373 (int) cur->type.max);
374 break;
375 case TUNABLE_TYPE_UINT_64:
376 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
377 (long int) cur->val.numval,
378 (long int) cur->type.min,
379 (long int) cur->type.max);
380 break;
381 case TUNABLE_TYPE_SIZE_T:
382 _dl_printf ("0x%Zx (min: 0x%Zx, max: 0x%Zx)\n",
383 (size_t) cur->val.numval,
384 (size_t) cur->type.min,
385 (size_t) cur->type.max);
386 break;
387 case TUNABLE_TYPE_STRING:
388 _dl_printf ("%s\n", cur->val.strval);
389 break;
390 default:
391 __builtin_unreachable ();
397 /* Set the tunable value. This is called by the module that the tunable exists
398 in. */
399 void
400 __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
402 tunable_t *cur = &tunable_list[id];
404 switch (cur->type.type_code)
406 case TUNABLE_TYPE_UINT_64:
408 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
409 break;
411 case TUNABLE_TYPE_INT_32:
413 *((int32_t *) valp) = (int32_t) cur->val.numval;
414 break;
416 case TUNABLE_TYPE_SIZE_T:
418 *((size_t *) valp) = (size_t) cur->val.numval;
419 break;
421 case TUNABLE_TYPE_STRING:
423 *((const char **)valp) = cur->val.strval;
424 break;
426 default:
427 __builtin_unreachable ();
430 if (cur->initialized && callback != NULL)
431 callback (&cur->val);
434 rtld_hidden_def (__tunable_get_val)