]> git.ipfire.org Git - thirdparty/glibc.git/blame - elf/dl-tunables.c
tst-pidfd.c: UNSUPPORTED if we get EPERM on valid pidfd_getfd call
[thirdparty/glibc.git] / elf / dl-tunables.c
CommitLineData
67e58f39
SP
1/* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
3
581c785b 4 Copyright (C) 2016-2022 Free Software Foundation, Inc.
67e58f39
SP
5 This file is part of the GNU C Library.
6
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.
11
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.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, see
5a82c748 19 <https://www.gnu.org/licenses/>. */
67e58f39 20
47618209
SN
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
086df229 25#include <startup.h>
67e58f39
SP
26#include <stdint.h>
27#include <stdbool.h>
28#include <unistd.h>
29#include <stdlib.h>
67e58f39
SP
30#include <sysdep.h>
31#include <fcntl.h>
32#include <ldsodefs.h>
86f65dff 33#include <array_length.h>
b05fae4d 34#include <dl-minimal-malloc.h>
67e58f39
SP
35
36#define TUNABLES_INTERNAL 1
37#include "dl-tunables.h"
38
95a73392
AZ
39#include <not-errno.h>
40
6765d5d3
SP
41#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
42# define GLIBC_TUNABLES "GLIBC_TUNABLES"
43#endif
9dd409a5 44
6765d5d3 45#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
9dd409a5
SP
46static char *
47tunables_strdup (const char *in)
48{
49 size_t i = 0;
50
51 while (in[i++] != '\0');
b05fae4d 52 char *out = __minimal_malloc (i + 1);
9dd409a5 53
a059f950
DD
54 /* For most of the tunables code, we ignore user errors. However,
55 this is a system error - and running out of memory at program
56 startup should be reported, so we do. */
b05fae4d
AZ
57 if (out == NULL)
58 _dl_fatal_printf ("failed to allocate memory to process tunables\n");
9dd409a5 59
9dd409a5
SP
60 while (i-- > 0)
61 out[i] = in[i];
62
63 return out;
64}
6765d5d3 65#endif
9dd409a5 66
67e58f39 67static char **
8b9e9c3c
SP
68get_next_env (char **envp, char **name, size_t *namelen, char **val,
69 char ***prev_envp)
67e58f39
SP
70{
71 while (envp != NULL && *envp != NULL)
72 {
8b9e9c3c 73 char **prev = envp;
41389c40 74 char *envline = *envp++;
67e58f39
SP
75 int len = 0;
76
77 while (envline[len] != '\0' && envline[len] != '=')
78 len++;
79
80 /* Just the name and no value, go to the next one. */
81 if (envline[len] == '\0')
82 continue;
83
84 *name = envline;
85 *namelen = len;
86 *val = &envline[len + 1];
8b9e9c3c 87 *prev_envp = prev;
67e58f39 88
41389c40 89 return envp;
67e58f39
SP
90 }
91
92 return NULL;
93}
94
67e58f39 95static void
61117bfa
SP
96do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
97 const tunable_num_t *minp,
98 const tunable_num_t *maxp)
67e58f39 99{
61117bfa 100 tunable_num_t val, min, max;
ad2f35cb 101
61117bfa
SP
102 if (cur->type.type_code == TUNABLE_TYPE_STRING)
103 {
104 cur->val.strval = valp->strval;
105 cur->initialized = true;
106 return;
107 }
ad2f35cb 108
d1a3dcab
SP
109 bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
110
61117bfa
SP
111 val = valp->numval;
112 min = minp != NULL ? *minp : cur->type.min;
113 max = maxp != NULL ? *maxp : cur->type.max;
114
115 /* We allow only increasingly restrictive bounds. */
d1a3dcab 116 if (tunable_val_lt (min, cur->type.min, unsigned_cmp))
61117bfa
SP
117 min = cur->type.min;
118
d1a3dcab 119 if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
61117bfa
SP
120 max = cur->type.max;
121
122 /* Skip both bounds if they're inconsistent. */
d1a3dcab 123 if (tunable_val_gt (min, max, unsigned_cmp))
67e58f39 124 {
61117bfa
SP
125 min = cur->type.min;
126 max = cur->type.max;
127 }
128
d1a3dcab
SP
129 /* Bail out if the bounds are not valid. */
130 if (tunable_val_lt (val, min, unsigned_cmp)
131 || tunable_val_lt (max, val, unsigned_cmp))
132 return;
133
134 cur->val.numval = val;
135 cur->type.min = min;
136 cur->type.max = max;
137 cur->initialized = true;
67e58f39
SP
138}
139
44330b6d
SP
140/* Validate range of the input value and initialize the tunable CUR if it looks
141 good. */
142static void
143tunable_initialize (tunable_t *cur, const char *strval)
144{
61117bfa 145 tunable_val_t val;
44330b6d
SP
146
147 if (cur->type.type_code != TUNABLE_TYPE_STRING)
61117bfa 148 val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
44330b6d 149 else
61117bfa
SP
150 val.strval = strval;
151 do_tunable_update_val (cur, &val, NULL, NULL);
44330b6d
SP
152}
153
154void
61117bfa
SP
155__tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
156 tunable_num_t *maxp)
44330b6d
SP
157{
158 tunable_t *cur = &tunable_list[id];
159
dfb8e514 160 do_tunable_update_val (cur, valp, minp, maxp);
44330b6d
SP
161}
162
6765d5d3 163#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
8b9e9c3c
SP
164/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
165 be unsafe for AT_SECURE processes so that it can be used as the new
166 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
167 environment variable string which we use to make NULL terminated values so
168 that we don't have to allocate memory again for it. */
9dd409a5 169static void
8b9e9c3c 170parse_tunables (char *tunestr, char *valstring)
9dd409a5
SP
171{
172 if (tunestr == NULL || *tunestr == '\0')
173 return;
174
175 char *p = tunestr;
2ed18c5b 176 size_t off = 0;
9dd409a5
SP
177
178 while (true)
179 {
180 char *name = p;
181 size_t len = 0;
182
183 /* First, find where the name ends. */
184 while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
185 len++;
186
187 /* If we reach the end of the string before getting a valid name-value
188 pair, bail out. */
189 if (p[len] == '\0')
2ed18c5b
SP
190 {
191 if (__libc_enable_secure)
192 tunestr[off] = '\0';
193 return;
194 }
9dd409a5
SP
195
196 /* We did not find a valid name-value pair before encountering the
197 colon. */
198 if (p[len]== ':')
199 {
200 p += len + 1;
201 continue;
202 }
203
204 p += len + 1;
205
8b9e9c3c
SP
206 /* Take the value from the valstring since we need to NULL terminate it. */
207 char *value = &valstring[p - tunestr];
9dd409a5
SP
208 len = 0;
209
210 while (p[len] != ':' && p[len] != '\0')
211 len++;
212
9dd409a5
SP
213 /* Add the tunable if it exists. */
214 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
215 {
216 tunable_t *cur = &tunable_list[i];
217
28cfa3a4 218 if (tunable_is_name (cur->name, name))
9dd409a5 219 {
2ed18c5b
SP
220 /* If we are in a secure context (AT_SECURE) then ignore the
221 tunable unless it is explicitly marked as secure. Tunable
222 values take precedence over their envvar aliases. We write
223 the tunables that are not SXID_ERASE back to TUNESTR, thus
224 dropping all SXID_ERASE tunables and any invalid or
225 unrecognized tunables. */
8b9e9c3c
SP
226 if (__libc_enable_secure)
227 {
2ed18c5b 228 if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
8b9e9c3c 229 {
2ed18c5b
SP
230 if (off > 0)
231 tunestr[off++] = ':';
232
233 const char *n = cur->name;
234
235 while (*n != '\0')
236 tunestr[off++] = *n++;
237
238 tunestr[off++] = '=';
239
240 for (size_t j = 0; j < len; j++)
241 tunestr[off++] = value[j];
8b9e9c3c
SP
242 }
243
244 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
245 break;
246 }
247
248 value[len] = '\0';
9dd409a5
SP
249 tunable_initialize (cur, value);
250 break;
251 }
252 }
253
2ed18c5b 254 if (p[len] != '\0')
8b9e9c3c 255 p += len + 1;
9dd409a5
SP
256 }
257}
6765d5d3 258#endif
9dd409a5 259
d054a81a
SP
260/* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
261 the system administrator has created the /etc/suid-debug file. This is a
262 special case where we want to conditionally enable/disable a tunable even
263 for setuid binaries. We use the special version of access() to avoid
264 setting ERRNO, which is a TLS variable since TLS has not yet been set
265 up. */
c2d8f0b7 266static __always_inline void
d054a81a 267maybe_enable_malloc_check (void)
67e58f39 268{
8b9e9c3c
SP
269 tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
270 if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
271 tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
67e58f39
SP
272}
273
274/* Initialize the tunables list from the environment. For now we only use the
275 ENV_ALIAS to find values. Later we will also use the tunable names to find
276 values. */
277void
278__tunables_init (char **envp)
279{
280 char *envname = NULL;
281 char *envval = NULL;
282 size_t len = 0;
8b9e9c3c 283 char **prev_envp = envp;
67e58f39 284
d054a81a 285 maybe_enable_malloc_check ();
67e58f39 286
8b9e9c3c
SP
287 while ((envp = get_next_env (envp, &envname, &len, &envval,
288 &prev_envp)) != NULL)
67e58f39 289 {
6765d5d3 290#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
28cfa3a4 291 if (tunable_is_name (GLIBC_TUNABLES, envname))
9dd409a5 292 {
8b9e9c3c
SP
293 char *new_env = tunables_strdup (envname);
294 if (new_env != NULL)
295 parse_tunables (new_env + len + 1, envval);
296 /* Put in the updated envval. */
297 *prev_envp = new_env;
9dd409a5
SP
298 continue;
299 }
6765d5d3 300#endif
9dd409a5 301
67e58f39
SP
302 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
303 {
304 tunable_t *cur = &tunable_list[i];
305
306 /* Skip over tunables that have either been set already or should be
307 skipped. */
bfe04789 308 if (cur->initialized || cur->env_alias[0] == '\0')
67e58f39
SP
309 continue;
310
311 const char *name = cur->env_alias;
312
313 /* We have a match. Initialize and move on to the next line. */
28cfa3a4 314 if (tunable_is_name (name, envname))
67e58f39 315 {
8b9e9c3c
SP
316 /* For AT_SECURE binaries, we need to check the security settings of
317 the tunable and decide whether we read the value and also whether
318 we erase the value so that child processes don't inherit them in
319 the environment. */
320 if (__libc_enable_secure)
321 {
322 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
323 {
324 /* Erase the environment variable. */
325 char **ep = prev_envp;
326
327 while (*ep != NULL)
328 {
28cfa3a4 329 if (tunable_is_name (name, *ep))
8b9e9c3c
SP
330 {
331 char **dp = ep;
332
333 do
334 dp[0] = dp[1];
335 while (*dp++);
336 }
337 else
338 ++ep;
339 }
340 /* Reset the iterator so that we read the environment again
341 from the point we erased. */
342 envp = prev_envp;
343 }
344
345 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
346 continue;
347 }
348
67e58f39
SP
349 tunable_initialize (cur, envval);
350 break;
351 }
352 }
353 }
354}
355
86f65dff
L
356void
357__tunables_print (void)
358{
359 for (int i = 0; i < array_length (tunable_list); i++)
360 {
361 const tunable_t *cur = &tunable_list[i];
362 if (cur->type.type_code == TUNABLE_TYPE_STRING
363 && cur->val.strval == NULL)
364 _dl_printf ("%s:\n", cur->name);
365 else
366 {
367 _dl_printf ("%s: ", cur->name);
368 switch (cur->type.type_code)
369 {
370 case TUNABLE_TYPE_INT_32:
371 _dl_printf ("%d (min: %d, max: %d)\n",
372 (int) cur->val.numval,
373 (int) cur->type.min,
374 (int) cur->type.max);
375 break;
376 case TUNABLE_TYPE_UINT_64:
377 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
378 (long int) cur->val.numval,
379 (long int) cur->type.min,
380 (long int) cur->type.max);
381 break;
382 case TUNABLE_TYPE_SIZE_T:
383 _dl_printf ("0x%Zx (min: 0x%Zx, max: 0x%Zx)\n",
384 (size_t) cur->val.numval,
385 (size_t) cur->type.min,
386 (size_t) cur->type.max);
387 break;
388 case TUNABLE_TYPE_STRING:
389 _dl_printf ("%s\n", cur->val.strval);
390 break;
391 default:
392 __builtin_unreachable ();
393 }
394 }
395 }
396}
397
67e58f39
SP
398/* Set the tunable value. This is called by the module that the tunable exists
399 in. */
400void
44330b6d 401__tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
67e58f39
SP
402{
403 tunable_t *cur = &tunable_list[id];
404
67e58f39
SP
405 switch (cur->type.type_code)
406 {
ad2f35cb
SP
407 case TUNABLE_TYPE_UINT_64:
408 {
409 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
410 break;
411 }
67e58f39
SP
412 case TUNABLE_TYPE_INT_32:
413 {
414 *((int32_t *) valp) = (int32_t) cur->val.numval;
415 break;
416 }
417 case TUNABLE_TYPE_SIZE_T:
418 {
419 *((size_t *) valp) = (size_t) cur->val.numval;
420 break;
421 }
422 case TUNABLE_TYPE_STRING:
423 {
424 *((const char **)valp) = cur->val.strval;
425 break;
426 }
427 default:
428 __builtin_unreachable ();
429 }
430
44330b6d 431 if (cur->initialized && callback != NULL)
67e58f39
SP
432 callback (&cur->val);
433}
81efada5 434
44330b6d 435rtld_hidden_def (__tunable_get_val)