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/>. */
25 #include <libc-internal.h>
30 #define TUNABLES_INTERNAL 1
31 #include "dl-tunables.h"
33 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
34 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
37 /* Compare environment or tunable names, bounded by the name hardcoded in
40 is_name (const char *orig
, const char *envname
)
42 for (;*orig
!= '\0' && *envname
!= '\0'; envname
++, orig
++)
43 if (*orig
!= *envname
)
46 /* The ENVNAME is immediately followed by a value. */
47 if (*orig
== '\0' && *envname
== '=')
53 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
55 tunables_strdup (const char *in
)
59 while (in
[i
++] != '\0');
60 char *out
= __sbrk (i
);
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
66 if (out
== (void *)-1)
79 get_next_env (char **envp
, char **name
, size_t *namelen
, char **val
)
81 while (envp
!= NULL
&& *envp
!= NULL
)
83 char *envline
= *envp
;
86 while (envline
[len
] != '\0' && envline
[len
] != '=')
89 /* Just the name and no value, go to the next one. */
90 if (envline
[len
] == '\0')
95 *val
= &envline
[len
+ 1];
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
)
109 unsigned long int result
= 0;
113 while (*nptr
== ' ' || *nptr
== '\t')
121 else if (*nptr
== '+')
124 if (*nptr
< '0' || *nptr
> '9')
131 if (nptr
[1] == 'x' || nptr
[1] == 'X')
145 unsigned long int digval
;
146 if (*nptr
>= '0' && *nptr
<= '0' + max_digit
)
147 digval
= *nptr
- '0';
150 if (*nptr
>= 'a' && *nptr
<= 'f')
151 digval
= *nptr
- 'a' + 10;
152 else if (*nptr
>= 'A' && *nptr
<= 'F')
153 digval
= *nptr
- 'A' + 10;
160 if (result
> ULONG_MAX
/ base
161 || (result
== ULONG_MAX
/ base
&& digval
> ULONG_MAX
% base
))
168 return result
* sign
;
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
175 tunable_set_val_if_valid_range (tunable_t
*cur
, const char *strval
,
176 int64_t default_min
, int64_t default_max
)
178 int64_t val
= tunables_strtoul (strval
);
180 int64_t min
= cur
->type
.min
;
181 int64_t max
= cur
->type
.max
;
189 if (val
>= min
&& val
<= max
)
191 cur
->val
.numval
= val
;
192 cur
->strval
= strval
;
196 /* Validate range of the input value and initialize the tunable CUR if it looks
199 tunable_initialize (tunable_t
*cur
, const char *strval
)
201 switch (cur
->type
.type_code
)
203 case TUNABLE_TYPE_INT_32
:
205 tunable_set_val_if_valid_range (cur
, strval
, INT32_MIN
, INT32_MAX
);
208 case TUNABLE_TYPE_SIZE_T
:
210 tunable_set_val_if_valid_range (cur
, strval
, 0, SIZE_MAX
);
213 case TUNABLE_TYPE_STRING
:
215 cur
->val
.strval
= cur
->strval
= strval
;
219 __builtin_unreachable ();
223 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
225 parse_tunables (char *tunestr
)
227 if (tunestr
== NULL
|| *tunestr
== '\0')
237 /* First, find where the name ends. */
238 while (p
[len
] != '=' && p
[len
] != ':' && p
[len
] != '\0')
241 /* If we reach the end of the string before getting a valid name-value
246 /* We did not find a valid name-value pair before encountering the
259 while (p
[len
] != ':' && p
[len
] != '\0')
265 /* Add the tunable if it exists. */
266 for (size_t i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
268 tunable_t
*cur
= &tunable_list
[i
];
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
)
276 if (is_name (cur
->name
, name
))
278 tunable_initialize (cur
, value
);
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
299 maybe_enable_malloc_check (void)
301 if (__access_noerrno ("/etc/suid-debug", F_OK
) == 0)
302 tunable_list
[TUNABLE_ENUM_NAME(glibc
, malloc
, check
)].is_secure
= true;
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
309 __tunables_init (char **envp
)
311 char *envname
= NULL
;
315 maybe_enable_malloc_check ();
317 while ((envp
= get_next_env (envp
, &envname
, &len
, &envval
)) != NULL
)
319 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
320 if (is_name (GLIBC_TUNABLES
, envname
))
322 char *val
= tunables_strdup (envval
);
324 parse_tunables (val
);
329 for (int i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
331 tunable_t
*cur
= &tunable_list
[i
];
333 /* Skip over tunables that have either been set already or should be
335 if (cur
->strval
!= NULL
|| cur
->env_alias
== NULL
336 || (__libc_enable_secure
&& !cur
->is_secure
))
339 const char *name
= cur
->env_alias
;
341 /* We have a match. Initialize and move on to the next line. */
342 if (is_name (name
, envname
))
344 tunable_initialize (cur
, envval
);
351 /* Set the tunable value. This is called by the module that the tunable exists
354 __tunable_set_val (tunable_id_t id
, void *valp
, tunable_callback_t callback
)
356 tunable_t
*cur
= &tunable_list
[id
];
358 /* Don't do anything if our tunable was not set during initialization or if
359 it failed validation. */
360 if (cur
->strval
== NULL
)
366 switch (cur
->type
.type_code
)
368 case TUNABLE_TYPE_INT_32
:
370 *((int32_t *) valp
) = (int32_t) cur
->val
.numval
;
373 case TUNABLE_TYPE_SIZE_T
:
375 *((size_t *) valp
) = (size_t) cur
->val
.numval
;
378 case TUNABLE_TYPE_STRING
:
380 *((const char **)valp
) = cur
->val
.strval
;
384 __builtin_unreachable ();
389 callback (&cur
->val
);