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