1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2018 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/>. */
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 /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
49 set the thread-local errno since the TCB has not yet been set up. This
50 needs to be fixed with an __sbrk implementation that does not set
52 if (out
== (void *)-1)
65 get_next_env (char **envp
, char **name
, size_t *namelen
, char **val
,
68 while (envp
!= NULL
&& *envp
!= NULL
)
71 char *envline
= *envp
++;
74 while (envline
[len
] != '\0' && envline
[len
] != '=')
77 /* Just the name and no value, go to the next one. */
78 if (envline
[len
] == '\0')
83 *val
= &envline
[len
+ 1];
92 #define TUNABLE_SET_VAL_IF_VALID_RANGE(__cur, __val, __type) \
94 __type min = (__cur)->type.min; \
95 __type max = (__cur)->type.max; \
97 if ((__type) (__val) >= min && (__type) (val) <= max) \
99 (__cur)->val.numval = val; \
100 (__cur)->initialized = true; \
105 do_tunable_update_val (tunable_t
*cur
, const void *valp
)
109 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
110 val
= *((int64_t *) valp
);
112 switch (cur
->type
.type_code
)
114 case TUNABLE_TYPE_INT_32
:
116 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, int64_t);
119 case TUNABLE_TYPE_UINT_64
:
121 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, uint64_t);
124 case TUNABLE_TYPE_SIZE_T
:
126 TUNABLE_SET_VAL_IF_VALID_RANGE (cur
, val
, uint64_t);
129 case TUNABLE_TYPE_STRING
:
131 cur
->val
.strval
= valp
;
135 __builtin_unreachable ();
139 /* Validate range of the input value and initialize the tunable CUR if it looks
142 tunable_initialize (tunable_t
*cur
, const char *strval
)
147 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
149 val
= _dl_strtoul (strval
, NULL
);
154 cur
->initialized
= true;
157 do_tunable_update_val (cur
, valp
);
161 __tunable_set_val (tunable_id_t id
, void *valp
)
163 tunable_t
*cur
= &tunable_list
[id
];
165 do_tunable_update_val (cur
, valp
);
168 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
169 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
170 be unsafe for AT_SECURE processes so that it can be used as the new
171 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
172 environment variable string which we use to make NULL terminated values so
173 that we don't have to allocate memory again for it. */
175 parse_tunables (char *tunestr
, char *valstring
)
177 if (tunestr
== NULL
|| *tunestr
== '\0')
187 /* First, find where the name ends. */
188 while (p
[len
] != '=' && p
[len
] != ':' && p
[len
] != '\0')
191 /* If we reach the end of the string before getting a valid name-value
196 /* We did not find a valid name-value pair before encountering the
206 /* Take the value from the valstring since we need to NULL terminate it. */
207 char *value
= &valstring
[p
- tunestr
];
210 while (p
[len
] != ':' && p
[len
] != '\0')
213 /* Add the tunable if it exists. */
214 for (size_t i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
216 tunable_t
*cur
= &tunable_list
[i
];
218 if (tunable_is_name (cur
->name
, name
))
220 /* If we are in a secure context (AT_SECURE) then ignore the tunable
221 unless it is explicitly marked as secure. Tunable values take
222 precendence over their envvar aliases. */
223 if (__libc_enable_secure
)
225 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
229 /* Last tunable in the valstring. Null-terminate and
236 /* Remove the current tunable from the string. We do
237 this by overwriting the string starting from NAME
238 (which is where the current tunable begins) with
239 the remainder of the string. We then have P point
240 to NAME so that we continue in the correct
241 position in the valstring. */
242 char *q
= &p
[len
+ 1];
251 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
256 tunable_initialize (cur
, value
);
269 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
270 the system administrator has created the /etc/suid-debug file. This is a
271 special case where we want to conditionally enable/disable a tunable even
272 for setuid binaries. We use the special version of access() to avoid
273 setting ERRNO, which is a TLS variable since TLS has not yet been set
277 maybe_enable_malloc_check (void)
279 tunable_id_t id
= TUNABLE_ENUM_NAME (glibc
, malloc
, check
);
280 if (__libc_enable_secure
&& __access_noerrno ("/etc/suid-debug", F_OK
) == 0)
281 tunable_list
[id
].security_level
= TUNABLE_SECLEVEL_NONE
;
284 /* Initialize the tunables list from the environment. For now we only use the
285 ENV_ALIAS to find values. Later we will also use the tunable names to find
288 __tunables_init (char **envp
)
290 char *envname
= NULL
;
293 char **prev_envp
= envp
;
295 maybe_enable_malloc_check ();
297 while ((envp
= get_next_env (envp
, &envname
, &len
, &envval
,
298 &prev_envp
)) != NULL
)
300 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
301 if (tunable_is_name (GLIBC_TUNABLES
, envname
))
303 char *new_env
= tunables_strdup (envname
);
305 parse_tunables (new_env
+ len
+ 1, envval
);
306 /* Put in the updated envval. */
307 *prev_envp
= new_env
;
312 for (int i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
314 tunable_t
*cur
= &tunable_list
[i
];
316 /* Skip over tunables that have either been set already or should be
318 if (cur
->initialized
|| cur
->env_alias
== NULL
)
321 const char *name
= cur
->env_alias
;
323 /* We have a match. Initialize and move on to the next line. */
324 if (tunable_is_name (name
, envname
))
326 /* For AT_SECURE binaries, we need to check the security settings of
327 the tunable and decide whether we read the value and also whether
328 we erase the value so that child processes don't inherit them in
330 if (__libc_enable_secure
)
332 if (cur
->security_level
== TUNABLE_SECLEVEL_SXID_ERASE
)
334 /* Erase the environment variable. */
335 char **ep
= prev_envp
;
339 if (tunable_is_name (name
, *ep
))
350 /* Reset the iterator so that we read the environment again
351 from the point we erased. */
355 if (cur
->security_level
!= TUNABLE_SECLEVEL_NONE
)
359 tunable_initialize (cur
, envval
);
366 /* Set the tunable value. This is called by the module that the tunable exists
369 __tunable_get_val (tunable_id_t id
, void *valp
, tunable_callback_t callback
)
371 tunable_t
*cur
= &tunable_list
[id
];
373 switch (cur
->type
.type_code
)
375 case TUNABLE_TYPE_UINT_64
:
377 *((uint64_t *) valp
) = (uint64_t) cur
->val
.numval
;
380 case TUNABLE_TYPE_INT_32
:
382 *((int32_t *) valp
) = (int32_t) cur
->val
.numval
;
385 case TUNABLE_TYPE_SIZE_T
:
387 *((size_t *) valp
) = (size_t) cur
->val
.numval
;
390 case TUNABLE_TYPE_STRING
:
392 *((const char **)valp
) = cur
->val
.strval
;
396 __builtin_unreachable ();
399 if (cur
->initialized
&& callback
!= NULL
)
400 callback (&cur
->val
);
403 rtld_hidden_def (__tunable_get_val
)