1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2023 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/>. */
21 /* Mark symbols hidden in static PIE for early self relocation to work. */
23 # pragma GCC visibility push(hidden)
33 #include <array_length.h>
34 #include <dl-minimal-malloc.h>
36 #define TUNABLES_INTERNAL 1
37 #include "dl-tunables.h"
39 #include <not-errno.h>
42 tunables_strdup (const char *in
)
46 while (in
[i
++] != '\0');
47 char *out
= __minimal_malloc (i
+ 1);
49 /* For most of the tunables code, we ignore user errors. However,
50 this is a system error - and running out of memory at program
51 startup should be reported, so we do. */
53 _dl_fatal_printf ("failed to allocate memory to process tunables\n");
62 get_next_env (char **envp
, char **name
, size_t *namelen
, char **val
,
65 while (envp
!= NULL
&& *envp
!= NULL
)
68 char *envline
= *envp
++;
71 while (envline
[len
] != '\0' && envline
[len
] != '=')
74 /* Just the name and no value, go to the next one. */
75 if (envline
[len
] == '\0')
80 *val
= &envline
[len
+ 1];
90 do_tunable_update_val (tunable_t
*cur
, const tunable_val_t
*valp
,
91 const tunable_num_t
*minp
,
92 const tunable_num_t
*maxp
)
94 tunable_num_t val
, min
, max
;
96 if (cur
->type
.type_code
== TUNABLE_TYPE_STRING
)
98 cur
->val
.strval
= valp
->strval
;
99 cur
->initialized
= true;
103 bool unsigned_cmp
= unsigned_tunable_type (cur
->type
.type_code
);
106 min
= minp
!= NULL
? *minp
: cur
->type
.min
;
107 max
= maxp
!= NULL
? *maxp
: cur
->type
.max
;
109 /* We allow only increasingly restrictive bounds. */
110 if (tunable_val_lt (min
, cur
->type
.min
, unsigned_cmp
))
113 if (tunable_val_gt (max
, cur
->type
.max
, unsigned_cmp
))
116 /* Skip both bounds if they're inconsistent. */
117 if (tunable_val_gt (min
, max
, unsigned_cmp
))
123 /* Bail out if the bounds are not valid. */
124 if (tunable_val_lt (val
, min
, unsigned_cmp
)
125 || tunable_val_lt (max
, val
, unsigned_cmp
))
128 cur
->val
.numval
= val
;
131 cur
->initialized
= true;
134 /* Validate range of the input value and initialize the tunable CUR if it looks
137 tunable_initialize (tunable_t
*cur
, const char *strval
)
141 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
142 val
.numval
= (tunable_num_t
) _dl_strtoul (strval
, NULL
);
145 do_tunable_update_val (cur
, &val
, NULL
, NULL
);
149 __tunable_set_val (tunable_id_t id
, tunable_val_t
*valp
, tunable_num_t
*minp
,
152 tunable_t
*cur
= &tunable_list
[id
];
154 do_tunable_update_val (cur
, valp
, minp
, maxp
);
157 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
158 be unsafe for AT_SECURE processes so that it can be used as the new
159 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
160 environment variable string which we use to make NULL terminated values so
161 that we don't have to allocate memory again for it. */
163 parse_tunables (char *tunestr
, char *valstring
)
165 if (tunestr
== NULL
|| *tunestr
== '\0')
176 /* First, find where the name ends. */
177 while (p
[len
] != '=' && p
[len
] != ':' && p
[len
] != '\0')
180 /* If we reach the end of the string before getting a valid name-value
184 if (__libc_enable_secure
)
189 /* We did not find a valid name-value pair before encountering the
199 /* Take the value from the valstring since we need to NULL terminate it. */
200 char *value
= &valstring
[p
- tunestr
];
203 while (p
[len
] != ':' && p
[len
] != '\0')
206 /* Add the tunable if it exists. */
207 for (size_t i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
209 tunable_t
*cur
= &tunable_list
[i
];
211 if (tunable_is_name (cur
->name
, name
))
213 /* If we are in a secure context (AT_SECURE) then ignore the
214 tunable unless it is explicitly marked as secure. Tunable
215 values take precedence over their envvar aliases. We write
216 the tunables that are not SXID_ERASE back to TUNESTR, thus
217 dropping all SXID_ERASE tunables and any invalid or
218 unrecognized tunables. */
219 if (__libc_enable_secure
)
221 if (cur
->security_level
!= TUNABLE_SECLEVEL_SXID_ERASE
)
224 tunestr
[off
++] = ':';
226 const char *n
= cur
->name
;
229 tunestr
[off
++] = *n
++;
231 tunestr
[off
++] = '=';
233 for (size_t j
= 0; j
< len
; j
++)
234 tunestr
[off
++] = value
[j
];
237 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
242 tunable_initialize (cur
, value
);
252 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
253 the system administrator has created the /etc/suid-debug file. This is a
254 special case where we want to conditionally enable/disable a tunable even
255 for setuid binaries. We use the special version of access() to avoid
256 setting ERRNO, which is a TLS variable since TLS has not yet been set
258 static __always_inline
void
259 maybe_enable_malloc_check (void)
261 tunable_id_t id
= TUNABLE_ENUM_NAME (glibc
, malloc
, check
);
262 if (__libc_enable_secure
&& __access_noerrno ("/etc/suid-debug", F_OK
) == 0)
263 tunable_list
[id
].security_level
= TUNABLE_SECLEVEL_NONE
;
266 /* Initialize the tunables list from the environment. For now we only use the
267 ENV_ALIAS to find values. Later we will also use the tunable names to find
270 __tunables_init (char **envp
)
272 char *envname
= NULL
;
275 char **prev_envp
= envp
;
277 maybe_enable_malloc_check ();
279 while ((envp
= get_next_env (envp
, &envname
, &len
, &envval
,
280 &prev_envp
)) != NULL
)
282 if (tunable_is_name ("GLIBC_TUNABLES", envname
))
284 char *new_env
= tunables_strdup (envname
);
286 parse_tunables (new_env
+ len
+ 1, envval
);
287 /* Put in the updated envval. */
288 *prev_envp
= new_env
;
292 for (int i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
294 tunable_t
*cur
= &tunable_list
[i
];
296 /* Skip over tunables that have either been set already or should be
298 if (cur
->initialized
|| cur
->env_alias
[0] == '\0')
301 const char *name
= cur
->env_alias
;
303 /* We have a match. Initialize and move on to the next line. */
304 if (tunable_is_name (name
, envname
))
306 /* For AT_SECURE binaries, we need to check the security settings of
307 the tunable and decide whether we read the value and also whether
308 we erase the value so that child processes don't inherit them in
310 if (__libc_enable_secure
)
312 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
314 /* Erase the environment variable. */
315 char **ep
= prev_envp
;
319 if (tunable_is_name (name
, *ep
))
330 /* Reset the iterator so that we read the environment again
331 from the point we erased. */
335 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
339 tunable_initialize (cur
, envval
);
347 __tunables_print (void)
349 for (int i
= 0; i
< array_length (tunable_list
); i
++)
351 const tunable_t
*cur
= &tunable_list
[i
];
352 if (cur
->type
.type_code
== TUNABLE_TYPE_STRING
353 && cur
->val
.strval
== NULL
)
354 _dl_printf ("%s:\n", cur
->name
);
357 _dl_printf ("%s: ", cur
->name
);
358 switch (cur
->type
.type_code
)
360 case TUNABLE_TYPE_INT_32
:
361 _dl_printf ("%d (min: %d, max: %d)\n",
362 (int) cur
->val
.numval
,
364 (int) cur
->type
.max
);
366 case TUNABLE_TYPE_UINT_64
:
367 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
368 (long int) cur
->val
.numval
,
369 (long int) cur
->type
.min
,
370 (long int) cur
->type
.max
);
372 case TUNABLE_TYPE_SIZE_T
:
373 _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
374 (size_t) cur
->val
.numval
,
375 (size_t) cur
->type
.min
,
376 (size_t) cur
->type
.max
);
378 case TUNABLE_TYPE_STRING
:
379 _dl_printf ("%s\n", cur
->val
.strval
);
382 __builtin_unreachable ();
388 /* Set the tunable value. This is called by the module that the tunable exists
391 __tunable_get_val (tunable_id_t id
, void *valp
, tunable_callback_t callback
)
393 tunable_t
*cur
= &tunable_list
[id
];
395 switch (cur
->type
.type_code
)
397 case TUNABLE_TYPE_UINT_64
:
399 *((uint64_t *) valp
) = (uint64_t) cur
->val
.numval
;
402 case TUNABLE_TYPE_INT_32
:
404 *((int32_t *) valp
) = (int32_t) cur
->val
.numval
;
407 case TUNABLE_TYPE_SIZE_T
:
409 *((size_t *) valp
) = (size_t) cur
->val
.numval
;
412 case TUNABLE_TYPE_STRING
:
414 *((const char **)valp
) = cur
->val
.strval
;
418 __builtin_unreachable ();
421 if (cur
->initialized
&& callback
!= NULL
)
422 callback (&cur
->val
);
425 rtld_hidden_def (__tunable_get_val
)