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