1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2017 Free Software Foundation, Inc.
5 This file is part of the GNU C Library.
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.
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.
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/>. */
29 #define TUNABLES_INTERNAL 1
30 #include "dl-tunables.h"
32 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
33 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
36 /* Compare environment or tunable names, bounded by the name hardcoded in
39 is_name (const char *orig
, const char *envname
)
41 for (;*orig
!= '\0' && *envname
!= '\0'; envname
++, orig
++)
42 if (*orig
!= *envname
)
45 /* The ENVNAME is immediately followed by a value. */
46 if (*orig
== '\0' && *envname
== '=')
52 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
54 tunables_strdup (const char *in
)
58 while (in
[i
++] != '\0');
59 char *out
= __sbrk (i
);
61 /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
62 set the thread-local errno since the TCB has not yet been set up. This
63 needs to be fixed with an __sbrk implementation that does not set
65 if (out
== (void *)-1)
78 get_next_env (char **envp
, char **name
, size_t *namelen
, char **val
,
81 while (envp
!= NULL
&& *envp
!= NULL
)
84 char *envline
= *envp
++;
87 while (envline
[len
] != '\0' && envline
[len
] != '=')
90 /* Just the name and no value, go to the next one. */
91 if (envline
[len
] == '\0')
96 *val
= &envline
[len
+ 1];
105 /* A stripped down strtoul-like implementation for very early use. It does not
106 set errno if the result is outside bounds because it gets called before
107 errno may have been set up. */
108 static unsigned long int
109 tunables_strtoul (const char *nptr
)
111 unsigned long int result
= 0;
115 while (*nptr
== ' ' || *nptr
== '\t')
123 else if (*nptr
== '+')
126 if (*nptr
< '0' || *nptr
> '9')
133 if (nptr
[1] == 'x' || nptr
[1] == 'X')
147 unsigned long int digval
;
148 if (*nptr
>= '0' && *nptr
<= '0' + max_digit
)
149 digval
= *nptr
- '0';
152 if (*nptr
>= 'a' && *nptr
<= 'f')
153 digval
= *nptr
- 'a' + 10;
154 else if (*nptr
>= 'A' && *nptr
<= 'F')
155 digval
= *nptr
- 'A' + 10;
162 if (result
> ULONG_MAX
/ base
163 || (result
== ULONG_MAX
/ base
&& digval
> ULONG_MAX
% base
))
170 return result
* sign
;
173 /* Initialize the internal type if the value validates either using the
174 explicit constraints of the tunable or with the implicit constraints of its
177 tunable_set_val_if_valid_range_signed (tunable_t
*cur
, const char *strval
,
178 int64_t default_min
, int64_t default_max
)
180 int64_t val
= (int64_t) tunables_strtoul (strval
);
182 int64_t min
= cur
->type
.min
;
183 int64_t max
= cur
->type
.max
;
191 if (val
>= min
&& val
<= max
)
193 cur
->val
.numval
= val
;
194 cur
->strval
= strval
;
199 tunable_set_val_if_valid_range_unsigned (tunable_t
*cur
, const char *strval
,
200 uint64_t default_min
, uint64_t default_max
)
202 uint64_t val
= (uint64_t) tunables_strtoul (strval
);
204 uint64_t min
= cur
->type
.min
;
205 uint64_t max
= cur
->type
.max
;
213 if (val
>= min
&& val
<= max
)
215 cur
->val
.numval
= val
;
216 cur
->strval
= strval
;
220 /* Validate range of the input value and initialize the tunable CUR if it looks
223 tunable_initialize (tunable_t
*cur
, const char *strval
)
225 switch (cur
->type
.type_code
)
227 case TUNABLE_TYPE_INT_32
:
229 tunable_set_val_if_valid_range_signed (cur
, strval
, INT32_MIN
, INT32_MAX
);
232 case TUNABLE_TYPE_SIZE_T
:
234 tunable_set_val_if_valid_range_unsigned (cur
, strval
, 0, SIZE_MAX
);
237 case TUNABLE_TYPE_STRING
:
239 cur
->val
.strval
= cur
->strval
= strval
;
243 __builtin_unreachable ();
247 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
248 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
249 be unsafe for AT_SECURE processes so that it can be used as the new
250 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
251 environment variable string which we use to make NULL terminated values so
252 that we don't have to allocate memory again for it. */
254 parse_tunables (char *tunestr
, char *valstring
)
256 if (tunestr
== NULL
|| *tunestr
== '\0')
266 /* First, find where the name ends. */
267 while (p
[len
] != '=' && p
[len
] != ':' && p
[len
] != '\0')
270 /* If we reach the end of the string before getting a valid name-value
275 /* We did not find a valid name-value pair before encountering the
285 /* Take the value from the valstring since we need to NULL terminate it. */
286 char *value
= &valstring
[p
- tunestr
];
289 while (p
[len
] != ':' && p
[len
] != '\0')
292 /* Add the tunable if it exists. */
293 for (size_t i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
295 tunable_t
*cur
= &tunable_list
[i
];
297 if (is_name (cur
->name
, name
))
299 /* If we are in a secure context (AT_SECURE) then ignore the tunable
300 unless it is explicitly marked as secure. Tunable values take
301 precendence over their envvar aliases. */
302 if (__libc_enable_secure
)
304 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
308 /* Last tunable in the valstring. Null-terminate and
315 /* Remove the current tunable from the string. We do
316 this by overwriting the string starting from NAME
317 (which is where the current tunable begins) with
318 the remainder of the string. We then have P point
319 to NAME so that we continue in the correct
320 position in the valstring. */
321 char *q
= &p
[len
+ 1];
330 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
335 tunable_initialize (cur
, value
);
348 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
349 the system administrator has created the /etc/suid-debug file. This is a
350 special case where we want to conditionally enable/disable a tunable even
351 for setuid binaries. We use the special version of access() to avoid
352 setting ERRNO, which is a TLS variable since TLS has not yet been set
356 maybe_enable_malloc_check (void)
358 tunable_id_t id
= TUNABLE_ENUM_NAME (glibc
, malloc
, check
);
359 if (__libc_enable_secure
&& __access_noerrno ("/etc/suid-debug", F_OK
) == 0)
360 tunable_list
[id
].security_level
= TUNABLE_SECLEVEL_NONE
;
363 /* Initialize the tunables list from the environment. For now we only use the
364 ENV_ALIAS to find values. Later we will also use the tunable names to find
367 __tunables_init (char **envp
)
369 char *envname
= NULL
;
372 char **prev_envp
= envp
;
374 maybe_enable_malloc_check ();
376 while ((envp
= get_next_env (envp
, &envname
, &len
, &envval
,
377 &prev_envp
)) != NULL
)
379 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
380 if (is_name (GLIBC_TUNABLES
, envname
))
382 char *new_env
= tunables_strdup (envname
);
384 parse_tunables (new_env
+ len
+ 1, envval
);
385 /* Put in the updated envval. */
386 *prev_envp
= new_env
;
391 for (int i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
393 tunable_t
*cur
= &tunable_list
[i
];
395 /* Skip over tunables that have either been set already or should be
397 if (cur
->strval
!= NULL
|| cur
->env_alias
== NULL
)
400 const char *name
= cur
->env_alias
;
402 /* We have a match. Initialize and move on to the next line. */
403 if (is_name (name
, envname
))
405 /* For AT_SECURE binaries, we need to check the security settings of
406 the tunable and decide whether we read the value and also whether
407 we erase the value so that child processes don't inherit them in
409 if (__libc_enable_secure
)
411 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
413 /* Erase the environment variable. */
414 char **ep
= prev_envp
;
418 if (is_name (name
, *ep
))
429 /* Reset the iterator so that we read the environment again
430 from the point we erased. */
434 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
438 tunable_initialize (cur
, envval
);
445 /* Set the tunable value. This is called by the module that the tunable exists
448 __tunable_set_val (tunable_id_t id
, void *valp
, tunable_callback_t callback
)
450 tunable_t
*cur
= &tunable_list
[id
];
452 /* Don't do anything if our tunable was not set during initialization or if
453 it failed validation. */
454 if (cur
->strval
== NULL
)
457 /* Caller does not need the value, just call the callback with our tunable
462 switch (cur
->type
.type_code
)
464 case TUNABLE_TYPE_INT_32
:
466 *((int32_t *) valp
) = (int32_t) cur
->val
.numval
;
469 case TUNABLE_TYPE_SIZE_T
:
471 *((size_t *) valp
) = (size_t) cur
->val
.numval
;
474 case TUNABLE_TYPE_STRING
:
476 *((const char **)valp
) = cur
->val
.strval
;
480 __builtin_unreachable ();
485 callback (&cur
->val
);