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