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