linux: Add FSCONFIG_CMD_CREATE_EXCL from Linux 6.6 to sys/mount.h
[glibc.git] / elf / dl-tunables.c
blobcae67efa0aacb9984c49874dcb95f170ecd1d86d
1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2023 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>
34 #include <dl-minimal-malloc.h>
36 #define TUNABLES_INTERNAL 1
37 #include "dl-tunables.h"
39 #include <not-errno.h>
41 static char *
42 tunables_strdup (const char *in)
44 size_t i = 0;
46 while (in[i++] != '\0');
47 char *out = __minimal_malloc (i + 1);
49 /* For most of the tunables code, we ignore user errors. However,
50 this is a system error - and running out of memory at program
51 startup should be reported, so we do. */
52 if (out == NULL)
53 _dl_fatal_printf ("failed to allocate memory to process tunables\n");
55 while (i-- > 0)
56 out[i] = in[i];
58 return out;
61 static char **
62 get_next_env (char **envp, char **name, size_t *namelen, char **val,
63 char ***prev_envp)
65 while (envp != NULL && *envp != NULL)
67 char **prev = envp;
68 char *envline = *envp++;
69 int len = 0;
71 while (envline[len] != '\0' && envline[len] != '=')
72 len++;
74 /* Just the name and no value, go to the next one. */
75 if (envline[len] == '\0')
76 continue;
78 *name = envline;
79 *namelen = len;
80 *val = &envline[len + 1];
81 *prev_envp = prev;
83 return envp;
86 return NULL;
89 static void
90 do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
91 const tunable_num_t *minp,
92 const tunable_num_t *maxp)
94 tunable_num_t val, min, max;
96 if (cur->type.type_code == TUNABLE_TYPE_STRING)
98 cur->val.strval = valp->strval;
99 cur->initialized = true;
100 return;
103 bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
105 val = valp->numval;
106 min = minp != NULL ? *minp : cur->type.min;
107 max = maxp != NULL ? *maxp : cur->type.max;
109 /* We allow only increasingly restrictive bounds. */
110 if (tunable_val_lt (min, cur->type.min, unsigned_cmp))
111 min = cur->type.min;
113 if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
114 max = cur->type.max;
116 /* Skip both bounds if they're inconsistent. */
117 if (tunable_val_gt (min, max, unsigned_cmp))
119 min = cur->type.min;
120 max = cur->type.max;
123 /* Bail out if the bounds are not valid. */
124 if (tunable_val_lt (val, min, unsigned_cmp)
125 || tunable_val_lt (max, val, unsigned_cmp))
126 return;
128 cur->val.numval = val;
129 cur->type.min = min;
130 cur->type.max = max;
131 cur->initialized = true;
134 /* Validate range of the input value and initialize the tunable CUR if it looks
135 good. */
136 static void
137 tunable_initialize (tunable_t *cur, const char *strval)
139 tunable_val_t val;
141 if (cur->type.type_code != TUNABLE_TYPE_STRING)
142 val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
143 else
144 val.strval = strval;
145 do_tunable_update_val (cur, &val, NULL, NULL);
148 void
149 __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
150 tunable_num_t *maxp)
152 tunable_t *cur = &tunable_list[id];
154 do_tunable_update_val (cur, valp, minp, maxp);
157 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
158 be unsafe for AT_SECURE processes so that it can be used as the new
159 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
160 environment variable string which we use to make NULL terminated values so
161 that we don't have to allocate memory again for it. */
162 static void
163 parse_tunables (char *tunestr, char *valstring)
165 if (tunestr == NULL || *tunestr == '\0')
166 return;
168 char *p = tunestr;
169 size_t off = 0;
171 while (true)
173 char *name = p;
174 size_t len = 0;
176 /* First, find where the name ends. */
177 while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178 len++;
180 /* If we reach the end of the string before getting a valid name-value
181 pair, bail out. */
182 if (p[len] == '\0')
183 break;
185 /* We did not find a valid name-value pair before encountering the
186 colon. */
187 if (p[len]== ':')
189 p += len + 1;
190 continue;
193 p += len + 1;
195 /* Take the value from the valstring since we need to NULL terminate it. */
196 char *value = &valstring[p - tunestr];
197 len = 0;
199 while (p[len] != ':' && p[len] != '\0')
200 len++;
202 /* Add the tunable if it exists. */
203 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
205 tunable_t *cur = &tunable_list[i];
207 if (tunable_is_name (cur->name, name))
209 /* If we are in a secure context (AT_SECURE) then ignore the
210 tunable unless it is explicitly marked as secure. Tunable
211 values take precedence over their envvar aliases. We write
212 the tunables that are not SXID_ERASE back to TUNESTR, thus
213 dropping all SXID_ERASE tunables and any invalid or
214 unrecognized tunables. */
215 if (__libc_enable_secure)
217 if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
219 if (off > 0)
220 tunestr[off++] = ':';
222 const char *n = cur->name;
224 while (*n != '\0')
225 tunestr[off++] = *n++;
227 tunestr[off++] = '=';
229 for (size_t j = 0; j < len; j++)
230 tunestr[off++] = value[j];
233 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
234 break;
237 value[len] = '\0';
238 tunable_initialize (cur, value);
239 break;
243 /* We reached the end while processing the tunable string. */
244 if (p[len] == '\0')
245 break;
247 p += len + 1;
250 /* Terminate tunestr before we leave. */
251 if (__libc_enable_secure)
252 tunestr[off] = '\0';
255 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
256 the system administrator has created the /etc/suid-debug file. This is a
257 special case where we want to conditionally enable/disable a tunable even
258 for setuid binaries. We use the special version of access() to avoid
259 setting ERRNO, which is a TLS variable since TLS has not yet been set
260 up. */
261 static __always_inline void
262 maybe_enable_malloc_check (void)
264 tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
265 if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
266 tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
269 /* Initialize the tunables list from the environment. For now we only use the
270 ENV_ALIAS to find values. Later we will also use the tunable names to find
271 values. */
272 void
273 __tunables_init (char **envp)
275 char *envname = NULL;
276 char *envval = NULL;
277 size_t len = 0;
278 char **prev_envp = envp;
280 maybe_enable_malloc_check ();
282 while ((envp = get_next_env (envp, &envname, &len, &envval,
283 &prev_envp)) != NULL)
285 if (tunable_is_name ("GLIBC_TUNABLES", envname))
287 char *new_env = tunables_strdup (envname);
288 if (new_env != NULL)
289 parse_tunables (new_env + len + 1, envval);
290 /* Put in the updated envval. */
291 *prev_envp = new_env;
292 continue;
295 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
297 tunable_t *cur = &tunable_list[i];
299 /* Skip over tunables that have either been set already or should be
300 skipped. */
301 if (cur->initialized || cur->env_alias[0] == '\0')
302 continue;
304 const char *name = cur->env_alias;
306 /* We have a match. Initialize and move on to the next line. */
307 if (tunable_is_name (name, envname))
309 /* For AT_SECURE binaries, we need to check the security settings of
310 the tunable and decide whether we read the value and also whether
311 we erase the value so that child processes don't inherit them in
312 the environment. */
313 if (__libc_enable_secure)
315 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
317 /* Erase the environment variable. */
318 char **ep = prev_envp;
320 while (*ep != NULL)
322 if (tunable_is_name (name, *ep))
324 char **dp = ep;
327 dp[0] = dp[1];
328 while (*dp++);
330 else
331 ++ep;
333 /* Reset the iterator so that we read the environment again
334 from the point we erased. */
335 envp = prev_envp;
338 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
339 continue;
342 tunable_initialize (cur, envval);
343 break;
349 void
350 __tunables_print (void)
352 for (int i = 0; i < array_length (tunable_list); i++)
354 const tunable_t *cur = &tunable_list[i];
355 if (cur->type.type_code == TUNABLE_TYPE_STRING
356 && cur->val.strval == NULL)
357 _dl_printf ("%s:\n", cur->name);
358 else
360 _dl_printf ("%s: ", cur->name);
361 switch (cur->type.type_code)
363 case TUNABLE_TYPE_INT_32:
364 _dl_printf ("%d (min: %d, max: %d)\n",
365 (int) cur->val.numval,
366 (int) cur->type.min,
367 (int) cur->type.max);
368 break;
369 case TUNABLE_TYPE_UINT_64:
370 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
371 (long int) cur->val.numval,
372 (long int) cur->type.min,
373 (long int) cur->type.max);
374 break;
375 case TUNABLE_TYPE_SIZE_T:
376 _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
377 (size_t) cur->val.numval,
378 (size_t) cur->type.min,
379 (size_t) cur->type.max);
380 break;
381 case TUNABLE_TYPE_STRING:
382 _dl_printf ("%s\n", cur->val.strval);
383 break;
384 default:
385 __builtin_unreachable ();
391 /* Set the tunable value. This is called by the module that the tunable exists
392 in. */
393 void
394 __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
396 tunable_t *cur = &tunable_list[id];
398 switch (cur->type.type_code)
400 case TUNABLE_TYPE_UINT_64:
402 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
403 break;
405 case TUNABLE_TYPE_INT_32:
407 *((int32_t *) valp) = (int32_t) cur->val.numval;
408 break;
410 case TUNABLE_TYPE_SIZE_T:
412 *((size_t *) valp) = (size_t) cur->val.numval;
413 break;
415 case TUNABLE_TYPE_STRING:
417 *((const char **)valp) = cur->val.strval;
418 break;
420 default:
421 __builtin_unreachable ();
424 if (cur->initialized && callback != NULL)
425 callback (&cur->val);
428 rtld_hidden_def (__tunable_get_val)