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 #include <not-errno.h>
34 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
35 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
38 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
40 tunables_strdup (const char *in
)
44 while (in
[i
++] != '\0');
45 char *out
= __sbrk (i
);
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
51 if (out
== (void *)-1)
64 get_next_env (char **envp
, char **name
, size_t *namelen
, char **val
,
67 while (envp
!= NULL
&& *envp
!= NULL
)
70 char *envline
= *envp
++;
73 while (envline
[len
] != '\0' && envline
[len
] != '=')
76 /* Just the name and no value, go to the next one. */
77 if (envline
[len
] == '\0')
82 *val
= &envline
[len
+ 1];
91 #define TUNABLE_SET_VAL_IF_VALID_RANGE(__cur, __val, __type, __default_min, \
94 __type min = (__cur)->type.min; \
95 __type max = (__cur)->type.max; \
99 min = __default_min; \
100 max = __default_max; \
103 if ((__type) (__val) >= min && (__type) (val) <= max) \
105 (__cur)->val.numval = val; \
106 (__cur)->initialized = true; \
111 do_tunable_update_val (tunable_t
*cur
, const void *valp
)
115 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
116 val
= *((int64_t *) valp
);
118 switch (cur
->type
.type_code
)
120 case TUNABLE_TYPE_INT_32
:
122 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, int64_t, INT32_MIN
, INT32_MAX
);
125 case TUNABLE_TYPE_UINT_64
:
127 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, uint64_t, 0, UINT64_MAX
);
130 case TUNABLE_TYPE_SIZE_T
:
132 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, uint64_t, 0, SIZE_MAX
);
135 case TUNABLE_TYPE_STRING
:
137 cur
->val
.strval
= valp
;
141 __builtin_unreachable ();
145 /* Validate range of the input value and initialize the tunable CUR if it looks
148 tunable_initialize (tunable_t
*cur
, const char *strval
)
153 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
155 val
= _dl_strtoul (strval
, NULL
);
160 cur
->initialized
= true;
163 do_tunable_update_val (cur
, valp
);
167 __tunable_set_val (tunable_id_t id
, void *valp
)
169 tunable_t
*cur
= &tunable_list
[id
];
171 do_tunable_update_val (cur
, valp
);
174 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
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. */
181 parse_tunables (char *tunestr
, char *valstring
)
183 if (tunestr
== NULL
|| *tunestr
== '\0')
193 /* First, find where the name ends. */
194 while (p
[len
] != '=' && p
[len
] != ':' && p
[len
] != '\0')
197 /* If we reach the end of the string before getting a valid name-value
202 /* We did not find a valid name-value pair before encountering the
212 /* Take the value from the valstring since we need to NULL terminate it. */
213 char *value
= &valstring
[p
- tunestr
];
216 while (p
[len
] != ':' && p
[len
] != '\0')
219 /* Add the tunable if it exists. */
220 for (size_t i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
222 tunable_t
*cur
= &tunable_list
[i
];
224 if (tunable_is_name (cur
->name
, name
))
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
)
231 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
235 /* Last tunable in the valstring. Null-terminate and
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];
257 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
262 tunable_initialize (cur
, value
);
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
283 maybe_enable_malloc_check (void)
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
;
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
294 __tunables_init (char **envp
)
296 char *envname
= NULL
;
299 char **prev_envp
= envp
;
301 maybe_enable_malloc_check ();
303 while ((envp
= get_next_env (envp
, &envname
, &len
, &envval
,
304 &prev_envp
)) != NULL
)
306 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
307 if (tunable_is_name (GLIBC_TUNABLES
, envname
))
309 char *new_env
= tunables_strdup (envname
);
311 parse_tunables (new_env
+ len
+ 1, envval
);
312 /* Put in the updated envval. */
313 *prev_envp
= new_env
;
318 for (int i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
320 tunable_t
*cur
= &tunable_list
[i
];
322 /* Skip over tunables that have either been set already or should be
324 if (cur
->initialized
|| cur
->env_alias
== NULL
)
327 const char *name
= cur
->env_alias
;
329 /* We have a match. Initialize and move on to the next line. */
330 if (tunable_is_name (name
, envname
))
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
336 if (__libc_enable_secure
)
338 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
340 /* Erase the environment variable. */
341 char **ep
= prev_envp
;
345 if (tunable_is_name (name
, *ep
))
356 /* Reset the iterator so that we read the environment again
357 from the point we erased. */
361 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
365 tunable_initialize (cur
, envval
);
372 /* Set the tunable value. This is called by the module that the tunable exists
375 __tunable_get_val (tunable_id_t id
, void *valp
, tunable_callback_t callback
)
377 tunable_t
*cur
= &tunable_list
[id
];
379 switch (cur
->type
.type_code
)
381 case TUNABLE_TYPE_UINT_64
:
383 *((uint64_t *) valp
) = (uint64_t) cur
->val
.numval
;
386 case TUNABLE_TYPE_INT_32
:
388 *((int32_t *) valp
) = (int32_t) cur
->val
.numval
;
391 case TUNABLE_TYPE_SIZE_T
:
393 *((size_t *) valp
) = (size_t) cur
->val
.numval
;
396 case TUNABLE_TYPE_STRING
:
398 *((const char **)valp
) = cur
->val
.strval
;
402 __builtin_unreachable ();
405 if (cur
->initialized
&& callback
!= NULL
)
406 callback (&cur
->val
);
409 rtld_hidden_def (__tunable_get_val
)