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