]> git.ipfire.org Git - thirdparty/glibc.git/blob - elf/dl-tunables.c
tunables: Avoid getenv calls and disable glibc.malloc.check by default
[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-2017 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 <http://www.gnu.org/licenses/>. */
20
21 #include <stdint.h>
22 #include <stdbool.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <libc-internal.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 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
34 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
35 #endif
36
37 /* Compare environment or tunable names, bounded by the name hardcoded in
38 glibc. */
39 static bool
40 is_name (const char *orig, const char *envname)
41 {
42 for (;*orig != '\0' && *envname != '\0'; envname++, orig++)
43 if (*orig != *envname)
44 break;
45
46 /* The ENVNAME is immediately followed by a value. */
47 if (*orig == '\0' && *envname == '=')
48 return true;
49 else
50 return false;
51 }
52
53 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
54 static char *
55 tunables_strdup (const char *in)
56 {
57 size_t i = 0;
58
59 while (in[i++] != '\0');
60 char *out = __sbrk (i);
61
62 /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
63 set the thread-local errno since the TCB has not yet been set up. This
64 needs to be fixed with an __sbrk implementation that does not set
65 errno. */
66 if (out == (void *)-1)
67 return NULL;
68
69 i--;
70
71 while (i-- > 0)
72 out[i] = in[i];
73
74 return out;
75 }
76 #endif
77
78 static char **
79 get_next_env (char **envp, char **name, size_t *namelen, char **val)
80 {
81 while (envp != NULL && *envp != NULL)
82 {
83 char *envline = *envp;
84 int len = 0;
85
86 while (envline[len] != '\0' && envline[len] != '=')
87 len++;
88
89 /* Just the name and no value, go to the next one. */
90 if (envline[len] == '\0')
91 continue;
92
93 *name = envline;
94 *namelen = len;
95 *val = &envline[len + 1];
96
97 return ++envp;
98 }
99
100 return NULL;
101 }
102
103 /* A stripped down strtoul-like implementation for very early use. It does not
104 set errno if the result is outside bounds because it gets called before
105 errno may have been set up. */
106 static unsigned long int
107 tunables_strtoul (const char *nptr)
108 {
109 unsigned long int result = 0;
110 long int sign = 1;
111 unsigned max_digit;
112
113 while (*nptr == ' ' || *nptr == '\t')
114 ++nptr;
115
116 if (*nptr == '-')
117 {
118 sign = -1;
119 ++nptr;
120 }
121 else if (*nptr == '+')
122 ++nptr;
123
124 if (*nptr < '0' || *nptr > '9')
125 return 0UL;
126
127 int base = 10;
128 max_digit = 9;
129 if (*nptr == '0')
130 {
131 if (nptr[1] == 'x' || nptr[1] == 'X')
132 {
133 base = 16;
134 nptr += 2;
135 }
136 else
137 {
138 base = 8;
139 max_digit = 7;
140 }
141 }
142
143 while (1)
144 {
145 unsigned long int digval;
146 if (*nptr >= '0' && *nptr <= '0' + max_digit)
147 digval = *nptr - '0';
148 else if (base == 16)
149 {
150 if (*nptr >= 'a' && *nptr <= 'f')
151 digval = *nptr - 'a' + 10;
152 else if (*nptr >= 'A' && *nptr <= 'F')
153 digval = *nptr - 'A' + 10;
154 else
155 break;
156 }
157 else
158 break;
159
160 if (result > ULONG_MAX / base
161 || (result == ULONG_MAX / base && digval > ULONG_MAX % base))
162 return ULONG_MAX;
163 result *= base;
164 result += digval;
165 ++nptr;
166 }
167
168 return result * sign;
169 }
170
171 /* Initialize the internal type if the value validates either using the
172 explicit constraints of the tunable or with the implicit constraints of its
173 type. */
174 static void
175 tunable_set_val_if_valid_range (tunable_t *cur, const char *strval,
176 int64_t default_min, int64_t default_max)
177 {
178 int64_t val = tunables_strtoul (strval);
179
180 int64_t min = cur->type.min;
181 int64_t max = cur->type.max;
182
183 if (min == max)
184 {
185 min = default_min;
186 max = default_max;
187 }
188
189 if (val >= min && val <= max)
190 {
191 cur->val.numval = val;
192 cur->strval = strval;
193 }
194 }
195
196 /* Validate range of the input value and initialize the tunable CUR if it looks
197 good. */
198 static void
199 tunable_initialize (tunable_t *cur, const char *strval)
200 {
201 switch (cur->type.type_code)
202 {
203 case TUNABLE_TYPE_INT_32:
204 {
205 tunable_set_val_if_valid_range (cur, strval, INT32_MIN, INT32_MAX);
206 break;
207 }
208 case TUNABLE_TYPE_SIZE_T:
209 {
210 tunable_set_val_if_valid_range (cur, strval, 0, SIZE_MAX);
211 break;
212 }
213 case TUNABLE_TYPE_STRING:
214 {
215 cur->val.strval = cur->strval = strval;
216 break;
217 }
218 default:
219 __builtin_unreachable ();
220 }
221 }
222
223 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
224 static void
225 parse_tunables (char *tunestr)
226 {
227 if (tunestr == NULL || *tunestr == '\0')
228 return;
229
230 char *p = tunestr;
231
232 while (true)
233 {
234 char *name = p;
235 size_t len = 0;
236
237 /* First, find where the name ends. */
238 while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
239 len++;
240
241 /* If we reach the end of the string before getting a valid name-value
242 pair, bail out. */
243 if (p[len] == '\0')
244 return;
245
246 /* We did not find a valid name-value pair before encountering the
247 colon. */
248 if (p[len]== ':')
249 {
250 p += len + 1;
251 continue;
252 }
253
254 p += len + 1;
255
256 char *value = p;
257 len = 0;
258
259 while (p[len] != ':' && p[len] != '\0')
260 len++;
261
262 char end = p[len];
263 p[len] = '\0';
264
265 /* Add the tunable if it exists. */
266 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
267 {
268 tunable_t *cur = &tunable_list[i];
269
270 /* If we are in a secure context (AT_SECURE) then ignore the tunable
271 unless it is explicitly marked as secure. Tunable values take
272 precendence over their envvar aliases. */
273 if (__libc_enable_secure && !cur->is_secure)
274 continue;
275
276 if (is_name (cur->name, name))
277 {
278 tunable_initialize (cur, value);
279 break;
280 }
281 }
282
283 if (end == ':')
284 p += len + 1;
285 else
286 return;
287 }
288 }
289 #endif
290
291 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
292 the system administrator has created the /etc/suid-debug file. This is a
293 special case where we want to conditionally enable/disable a tunable even
294 for setuid binaries. We use the special version of access() to avoid
295 setting ERRNO, which is a TLS variable since TLS has not yet been set
296 up. */
297 static inline void
298 __always_inline
299 maybe_enable_malloc_check (void)
300 {
301 if (__access_noerrno ("/etc/suid-debug", F_OK) == 0)
302 tunable_list[TUNABLE_ENUM_NAME(glibc, malloc, check)].is_secure = true;
303 }
304
305 /* Initialize the tunables list from the environment. For now we only use the
306 ENV_ALIAS to find values. Later we will also use the tunable names to find
307 values. */
308 void
309 __tunables_init (char **envp)
310 {
311 char *envname = NULL;
312 char *envval = NULL;
313 size_t len = 0;
314
315 maybe_enable_malloc_check ();
316
317 while ((envp = get_next_env (envp, &envname, &len, &envval)) != NULL)
318 {
319 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
320 if (is_name (GLIBC_TUNABLES, envname))
321 {
322 char *val = tunables_strdup (envval);
323 if (val != NULL)
324 parse_tunables (val);
325 continue;
326 }
327 #endif
328
329 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
330 {
331 tunable_t *cur = &tunable_list[i];
332
333 /* Skip over tunables that have either been set already or should be
334 skipped. */
335 if (cur->strval != NULL || cur->env_alias == NULL
336 || (__libc_enable_secure && !cur->is_secure))
337 continue;
338
339 const char *name = cur->env_alias;
340
341 /* We have a match. Initialize and move on to the next line. */
342 if (is_name (name, envname))
343 {
344 tunable_initialize (cur, envval);
345 break;
346 }
347 }
348 }
349 }
350
351 /* Set the tunable value. This is called by the module that the tunable exists
352 in. */
353 void
354 __tunable_set_val (tunable_id_t id, void *valp, tunable_callback_t callback)
355 {
356 tunable_t *cur = &tunable_list[id];
357
358 /* Don't do anything if our tunable was not set during initialization or if
359 it failed validation. */
360 if (cur->strval == NULL)
361 return;
362
363 if (valp == NULL)
364 goto cb;
365
366 switch (cur->type.type_code)
367 {
368 case TUNABLE_TYPE_INT_32:
369 {
370 *((int32_t *) valp) = (int32_t) cur->val.numval;
371 break;
372 }
373 case TUNABLE_TYPE_SIZE_T:
374 {
375 *((size_t *) valp) = (size_t) cur->val.numval;
376 break;
377 }
378 case TUNABLE_TYPE_STRING:
379 {
380 *((const char **)valp) = cur->val.strval;
381 break;
382 }
383 default:
384 __builtin_unreachable ();
385 }
386
387 cb:
388 if (callback)
389 callback (&cur->val);
390 }