1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2020 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 <https://www.gnu.org/licenses/>. */
30 #define TUNABLES_INTERNAL 1
31 #include "dl-tunables.h"
33 #include <not-errno.h>
35 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
36 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
39 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
41 tunables_strdup (const char *in
)
45 while (in
[i
++] != '\0');
46 char *out
= __sbrk (i
);
48 /* For most of the tunables code, we ignore user errors. However,
49 this is a system error - and running out of memory at program
50 startup should be reported, so we do. */
51 if (out
== (void *)-1)
52 _dl_fatal_printf ("sbrk() failure while processing tunables\n");
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) \
93 __type min = (__cur)->type.min; \
94 __type max = (__cur)->type.max; \
96 if ((__type) (__val) >= min && (__type) (val) <= max) \
98 (__cur)->val.numval = val; \
99 (__cur)->initialized = true; \
104 do_tunable_update_val (tunable_t
*cur
, const void *valp
)
108 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
109 val
= *((int64_t *) valp
);
111 switch (cur
->type
.type_code
)
113 case TUNABLE_TYPE_INT_32
:
115 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, int64_t);
118 case TUNABLE_TYPE_UINT_64
:
120 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, uint64_t);
123 case TUNABLE_TYPE_SIZE_T
:
125 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, uint64_t);
128 case TUNABLE_TYPE_STRING
:
130 cur
->val
.strval
= valp
;
134 __builtin_unreachable ();
138 /* Validate range of the input value and initialize the tunable CUR if it looks
141 tunable_initialize (tunable_t
*cur
, const char *strval
)
146 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
148 val
= _dl_strtoul (strval
, NULL
);
153 cur
->initialized
= true;
156 do_tunable_update_val (cur
, valp
);
160 __tunable_set_val (tunable_id_t id
, void *valp
)
162 tunable_t
*cur
= &tunable_list
[id
];
164 do_tunable_update_val (cur
, valp
);
167 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
168 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
169 be unsafe for AT_SECURE processes so that it can be used as the new
170 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
171 environment variable string which we use to make NULL terminated values so
172 that we don't have to allocate memory again for it. */
174 parse_tunables (char *tunestr
, char *valstring
)
176 if (tunestr
== NULL
|| *tunestr
== '\0')
186 /* First, find where the name ends. */
187 while (p
[len
] != '=' && p
[len
] != ':' && p
[len
] != '\0')
190 /* If we reach the end of the string before getting a valid name-value
195 /* We did not find a valid name-value pair before encountering the
205 /* Take the value from the valstring since we need to NULL terminate it. */
206 char *value
= &valstring
[p
- tunestr
];
209 while (p
[len
] != ':' && p
[len
] != '\0')
212 /* Add the tunable if it exists. */
213 for (size_t i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
215 tunable_t
*cur
= &tunable_list
[i
];
217 if (tunable_is_name (cur
->name
, name
))
219 /* If we are in a secure context (AT_SECURE) then ignore the tunable
220 unless it is explicitly marked as secure. Tunable values take
221 precendence over their envvar aliases. */
222 if (__libc_enable_secure
)
224 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
228 /* Last tunable in the valstring. Null-terminate and
235 /* Remove the current tunable from the string. We do
236 this by overwriting the string starting from NAME
237 (which is where the current tunable begins) with
238 the remainder of the string. We then have P point
239 to NAME so that we continue in the correct
240 position in the valstring. */
241 char *q
= &p
[len
+ 1];
250 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
255 tunable_initialize (cur
, value
);
268 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
269 the system administrator has created the /etc/suid-debug file. This is a
270 special case where we want to conditionally enable/disable a tunable even
271 for setuid binaries. We use the special version of access() to avoid
272 setting ERRNO, which is a TLS variable since TLS has not yet been set
274 static __always_inline
void
275 maybe_enable_malloc_check (void)
277 tunable_id_t id
= TUNABLE_ENUM_NAME (glibc
, malloc
, check
);
278 if (__libc_enable_secure
&& __access_noerrno ("/etc/suid-debug", F_OK
) == 0)
279 tunable_list
[id
].security_level
= TUNABLE_SECLEVEL_NONE
;
282 /* Initialize the tunables list from the environment. For now we only use the
283 ENV_ALIAS to find values. Later we will also use the tunable names to find
286 __tunables_init (char **envp
)
288 char *envname
= NULL
;
291 char **prev_envp
= envp
;
293 maybe_enable_malloc_check ();
295 while ((envp
= get_next_env (envp
, &envname
, &len
, &envval
,
296 &prev_envp
)) != NULL
)
298 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
299 if (tunable_is_name (GLIBC_TUNABLES
, envname
))
301 char *new_env
= tunables_strdup (envname
);
303 parse_tunables (new_env
+ len
+ 1, envval
);
304 /* Put in the updated envval. */
305 *prev_envp
= new_env
;
310 for (int i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
312 tunable_t
*cur
= &tunable_list
[i
];
314 /* Skip over tunables that have either been set already or should be
316 if (cur
->initialized
|| cur
->env_alias
== NULL
)
319 const char *name
= cur
->env_alias
;
321 /* We have a match. Initialize and move on to the next line. */
322 if (tunable_is_name (name
, envname
))
324 /* For AT_SECURE binaries, we need to check the security settings of
325 the tunable and decide whether we read the value and also whether
326 we erase the value so that child processes don't inherit them in
328 if (__libc_enable_secure
)
330 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
332 /* Erase the environment variable. */
333 char **ep
= prev_envp
;
337 if (tunable_is_name (name
, *ep
))
348 /* Reset the iterator so that we read the environment again
349 from the point we erased. */
353 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
357 tunable_initialize (cur
, envval
);
364 /* Set the tunable value. This is called by the module that the tunable exists
367 __tunable_get_val (tunable_id_t id
, void *valp
, tunable_callback_t callback
)
369 tunable_t
*cur
= &tunable_list
[id
];
371 switch (cur
->type
.type_code
)
373 case TUNABLE_TYPE_UINT_64
:
375 *((uint64_t *) valp
) = (uint64_t) cur
->val
.numval
;
378 case TUNABLE_TYPE_INT_32
:
380 *((int32_t *) valp
) = (int32_t) cur
->val
.numval
;
383 case TUNABLE_TYPE_SIZE_T
:
385 *((size_t *) valp
) = (size_t) cur
->val
.numval
;
388 case TUNABLE_TYPE_STRING
:
390 *((const char **)valp
) = cur
->val
.strval
;
394 __builtin_unreachable ();
397 if (cur
->initialized
&& callback
!= NULL
)
398 callback (&cur
->val
);
401 rtld_hidden_def (__tunable_get_val
)