elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries
[glibc.git] / elf / dl-tunables.c
blobf7dca8f7c130a30a2092ec280dbd46631f0f15a0
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. */
22 #if BUILD_PIE_DEFAULT
23 # pragma GCC visibility push(hidden)
24 #endif
25 #include <startup.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <sysdep.h>
31 #include <fcntl.h>
32 #include <ldsodefs.h>
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>
41 static char *
42 tunables_strdup (const char *in)
44 size_t i = 0;
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. */
52 if (out == NULL)
53 _dl_fatal_printf ("failed to allocate memory to process tunables\n");
55 while (i-- > 0)
56 out[i] = in[i];
58 return out;
61 static char **
62 get_next_env (char **envp, char **name, size_t *namelen, char **val,
63 char ***prev_envp)
65 while (envp != NULL && *envp != NULL)
67 char **prev = envp;
68 char *envline = *envp++;
69 int len = 0;
71 while (envline[len] != '\0' && envline[len] != '=')
72 len++;
74 /* Just the name and no value, go to the next one. */
75 if (envline[len] == '\0')
76 continue;
78 *name = envline;
79 *namelen = len;
80 *val = &envline[len + 1];
81 *prev_envp = prev;
83 return envp;
86 return NULL;
89 static void
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;
100 return;
103 bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
105 val = valp->numval;
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))
111 min = cur->type.min;
113 if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
114 max = cur->type.max;
116 /* Skip both bounds if they're inconsistent. */
117 if (tunable_val_gt (min, max, unsigned_cmp))
119 min = cur->type.min;
120 max = cur->type.max;
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))
126 return;
128 cur->val.numval = val;
129 cur->type.min = min;
130 cur->type.max = max;
131 cur->initialized = true;
134 /* Validate range of the input value and initialize the tunable CUR if it looks
135 good. */
136 static void
137 tunable_initialize (tunable_t *cur, const char *strval)
139 tunable_val_t val;
141 if (cur->type.type_code != TUNABLE_TYPE_STRING)
142 val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
143 else
144 val.strval = strval;
145 do_tunable_update_val (cur, &val, NULL, NULL);
148 void
149 __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
150 tunable_num_t *maxp)
152 tunable_t *cur = &tunable_list[id];
154 do_tunable_update_val (cur, valp, minp, maxp);
157 /* Parse the tunable string VALSTRING. VALSTRING is a duplicated value,
158 where delimiters ':' are replaced with '\0', so string tunables are null
159 terminated. */
160 static void
161 parse_tunables (char *valstring)
163 if (valstring == NULL || *valstring == '\0')
164 return;
166 char *p = valstring;
167 bool done = false;
169 while (!done)
171 char *name = p;
173 /* First, find where the name ends. */
174 while (*p != '=' && *p != ':' && *p != '\0')
175 p++;
177 /* If we reach the end of the string before getting a valid name-value
178 pair, bail out. */
179 if (*p == '\0')
180 break;
182 /* We did not find a valid name-value pair before encountering the
183 colon. */
184 if (*p == ':')
186 p++;
187 continue;
190 /* Skip the '='. */
191 p++;
193 const char *value = p;
195 while (*p != ':' && *p != '\0')
196 p++;
198 if (*p == '\0')
199 done = true;
200 else
201 *p++ = '\0';
203 /* Add the tunable if it exists. */
204 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
206 tunable_t *cur = &tunable_list[i];
208 if (tunable_is_name (cur->name, name))
210 tunable_initialize (cur, value);
211 break;
217 /* Initialize the tunables list from the environment. For now we only use the
218 ENV_ALIAS to find values. Later we will also use the tunable names to find
219 values. */
220 void
221 __tunables_init (char **envp)
223 char *envname = NULL;
224 char *envval = NULL;
225 size_t len = 0;
226 char **prev_envp = envp;
228 /* Ignore tunables for AT_SECURE programs. */
229 if (__libc_enable_secure)
230 return;
232 while ((envp = get_next_env (envp, &envname, &len, &envval,
233 &prev_envp)) != NULL)
235 if (tunable_is_name ("GLIBC_TUNABLES", envname))
237 parse_tunables (tunables_strdup (envval));
238 continue;
241 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
243 tunable_t *cur = &tunable_list[i];
245 /* Skip over tunables that have either been set already or should be
246 skipped. */
247 if (cur->initialized || cur->env_alias[0] == '\0')
248 continue;
250 const char *name = cur->env_alias;
252 /* We have a match. Initialize and move on to the next line. */
253 if (tunable_is_name (name, envname))
255 tunable_initialize (cur, envval);
256 break;
262 void
263 __tunables_print (void)
265 for (int i = 0; i < array_length (tunable_list); i++)
267 const tunable_t *cur = &tunable_list[i];
268 if (cur->type.type_code == TUNABLE_TYPE_STRING
269 && cur->val.strval == NULL)
270 _dl_printf ("%s:\n", cur->name);
271 else
273 _dl_printf ("%s: ", cur->name);
274 switch (cur->type.type_code)
276 case TUNABLE_TYPE_INT_32:
277 _dl_printf ("%d (min: %d, max: %d)\n",
278 (int) cur->val.numval,
279 (int) cur->type.min,
280 (int) cur->type.max);
281 break;
282 case TUNABLE_TYPE_UINT_64:
283 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
284 (long int) cur->val.numval,
285 (long int) cur->type.min,
286 (long int) cur->type.max);
287 break;
288 case TUNABLE_TYPE_SIZE_T:
289 _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
290 (size_t) cur->val.numval,
291 (size_t) cur->type.min,
292 (size_t) cur->type.max);
293 break;
294 case TUNABLE_TYPE_STRING:
295 _dl_printf ("%s\n", cur->val.strval);
296 break;
297 default:
298 __builtin_unreachable ();
304 /* Set the tunable value. This is called by the module that the tunable exists
305 in. */
306 void
307 __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
309 tunable_t *cur = &tunable_list[id];
311 switch (cur->type.type_code)
313 case TUNABLE_TYPE_UINT_64:
315 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
316 break;
318 case TUNABLE_TYPE_INT_32:
320 *((int32_t *) valp) = (int32_t) cur->val.numval;
321 break;
323 case TUNABLE_TYPE_SIZE_T:
325 *((size_t *) valp) = (size_t) cur->val.numval;
326 break;
328 case TUNABLE_TYPE_STRING:
330 *((const char **)valp) = cur->val.strval;
331 break;
333 default:
334 __builtin_unreachable ();
337 if (cur->initialized && callback != NULL)
338 callback (&cur->val);
341 rtld_hidden_def (__tunable_get_val)