1/* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
3
4 Copyright (C) 2016-2024 Free Software Foundation, Inc.
5 This file is part of the GNU C Library.
6
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.
11
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.
16
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/>. */
20
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>
35#include <dl-symbol-redir-ifunc.h>
36
37#define TUNABLES_INTERNAL 1
38#include "dl-tunables.h"
39
40static char **
41get_next_env (char **envp, char **name, char **val, char ***prev_envp)
42{
43 while (envp != NULL && *envp != NULL)
44 {
45 char **prev = envp;
46 char *envline = *envp++;
47 int len = 0;
48
49 while (envline[len] != '\0' && envline[len] != '=')
50 len++;
51
52 /* Just the name and no value, go to the next one. */
53 if (envline[len] == '\0')
54 continue;
55
56 *name = envline;
57 *val = &envline[len + 1];
58 *prev_envp = prev;
59
60 return envp;
61 }
62
63 return NULL;
64}
65
66static void
67do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
68 const tunable_num_t *minp,
69 const tunable_num_t *maxp)
70{
71 tunable_num_t val, min, max;
72
73 switch (cur->type.type_code)
74 {
75 case TUNABLE_TYPE_STRING:
76 cur->val.strval = valp->strval;
77 cur->initialized = true;
78 return;
79 case TUNABLE_TYPE_INT_32:
80 val = (int32_t) valp->numval;
81 break;
82 case TUNABLE_TYPE_UINT_64:
83 val = (int64_t) valp->numval;
84 break;
85 case TUNABLE_TYPE_SIZE_T:
86 val = (size_t) valp->numval;
87 break;
88 default:
89 __builtin_unreachable ();
90 }
91
92 bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
93
94 min = minp != NULL ? *minp : cur->type.min;
95 max = maxp != NULL ? *maxp : cur->type.max;
96
97 /* We allow only increasingly restrictive bounds. */
98 if (tunable_val_lt (lhs: min, rhs: cur->type.min, unsigned_cmp))
99 min = cur->type.min;
100
101 if (tunable_val_gt (lhs: max, rhs: cur->type.max, unsigned_cmp))
102 max = cur->type.max;
103
104 /* Skip both bounds if they're inconsistent. */
105 if (tunable_val_gt (lhs: min, rhs: max, unsigned_cmp))
106 {
107 min = cur->type.min;
108 max = cur->type.max;
109 }
110
111 /* Bail out if the bounds are not valid. */
112 if (tunable_val_lt (lhs: val, rhs: min, unsigned_cmp)
113 || tunable_val_lt (lhs: max, rhs: val, unsigned_cmp))
114 return;
115
116 cur->val.numval = val;
117 cur->type.min = min;
118 cur->type.max = max;
119 cur->initialized = true;
120}
121
122/* Validate range of the input value and initialize the tunable CUR if it looks
123 good. */
124static bool
125tunable_initialize (tunable_t *cur, const char *strval, size_t len)
126{
127 tunable_val_t val = { 0 };
128
129 if (cur->type.type_code != TUNABLE_TYPE_STRING)
130 {
131 char *endptr = NULL;
132 uint64_t numval = _dl_strtoul (strval, &endptr);
133 if (endptr != strval + len)
134 return false;
135 val.numval = (tunable_num_t) numval;
136 }
137 else
138 val.strval = (struct tunable_str_t) { strval, len };
139 do_tunable_update_val (cur, &val, NULL, NULL);
140
141 return true;
142}
143
144bool
145__tunable_is_initialized (tunable_id_t id)
146{
147 return tunable_list[id].initialized;
148}
149rtld_hidden_def (__tunable_is_initialized)
150
151void
152__tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
153 tunable_num_t *maxp)
154{
155 tunable_t *cur = &tunable_list[id];
156
157 do_tunable_update_val (cur, valp, minp, maxp);
158}
159
160struct tunable_toset_t
161{
162 tunable_t *t;
163 const char *value;
164 size_t len;
165};
166
167enum { tunables_list_size = array_length (tunable_list) };
168
169/* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
170 and their respective values. The VALSTRING is parsed in place, with the
171 tunable start and size recorded in TUNABLES.
172 Return the number of tunables found (including 0 if the string is empty)
173 or -1 if for an ill-formatted definition. */
174static int
175parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
176{
177 if (valstring == NULL || *valstring == '\0')
178 return 0;
179
180 const char *p = valstring;
181 bool done = false;
182 int ntunables = 0;
183
184 while (!done)
185 {
186 const char *name = p;
187
188 /* First, find where the name ends. */
189 while (*p != '=' && *p != ':' && *p != '\0')
190 p++;
191
192 /* If we reach the end of the string before getting a valid name-value
193 pair, bail out. */
194 if (*p == '\0')
195 return -1;
196
197 /* We did not find a valid name-value pair before encountering the
198 colon. */
199 if (*p == ':')
200 {
201 p++;
202 continue;
203 }
204
205 /* Skip the '='. */
206 p++;
207
208 const char *value = p;
209
210 while (*p != '=' && *p != ':' && *p != '\0')
211 p++;
212
213 if (*p == '=')
214 return -1;
215 else if (*p == '\0')
216 done = true;
217
218 /* Add the tunable if it exists. */
219 for (size_t i = 0; i < tunables_list_size; i++)
220 {
221 tunable_t *cur = &tunable_list[i];
222
223 if (tunable_is_name (orig: cur->name, envname: name))
224 {
225 tunables[ntunables++] =
226 (struct tunable_toset_t) { cur, value, p - value };
227
228 break;
229 }
230 }
231 }
232
233 return ntunables;
234}
235
236static void
237parse_tunables (const char *valstring)
238{
239 struct tunable_toset_t tunables[tunables_list_size] = { 0 };
240 if (parse_tunables_string (valstring, tunables) == -1)
241 {
242 _dl_error_printf (
243 fmt: "WARNING: ld.so: invalid GLIBC_TUNABLES `%s': ignored.\n", valstring);
244 return;
245 }
246
247 for (int i = 0; i < tunables_list_size; i++)
248 {
249 if (tunables[i].t == NULL)
250 continue;
251
252 if (!tunable_initialize (tunables[i].t, tunables[i].value,
253 tunables[i].len))
254 _dl_error_printf (fmt: "WARNING: ld.so: invalid GLIBC_TUNABLES value `%.*s' "
255 "for option `%s': ignored.\n",
256 (int) tunables[i].len,
257 tunables[i].value,
258 tunables[i].t->name);
259 }
260}
261
262/* Initialize the tunables list from the environment. For now we only use the
263 ENV_ALIAS to find values. Later we will also use the tunable names to find
264 values. */
265void
266__tunables_init (char **envp)
267{
268 char *envname = NULL;
269 char *envval = NULL;
270 char **prev_envp = envp;
271
272 /* Ignore tunables for AT_SECURE programs. */
273 if (__libc_enable_secure)
274 return;
275
276 while ((envp = get_next_env (envp, name: &envname, val: &envval, prev_envp: &prev_envp)) != NULL)
277 {
278 /* The environment variable is allocated on the stack by the kernel, so
279 it is safe to keep the references to the suboptions for later parsing
280 of string tunables. */
281 if (tunable_is_name (orig: "GLIBC_TUNABLES", envname))
282 {
283 parse_tunables (valstring: envval);
284 continue;
285 }
286
287 for (int i = 0; i < tunables_list_size; i++)
288 {
289 tunable_t *cur = &tunable_list[i];
290
291 /* Skip over tunables that have either been set already or should be
292 skipped. */
293 if (cur->initialized || cur->env_alias[0] == '\0')
294 continue;
295
296 const char *name = cur->env_alias;
297
298 /* We have a match. Initialize and move on to the next line. */
299 if (tunable_is_name (orig: name, envname))
300 {
301 size_t envvallen = 0;
302 /* The environment variable is always null-terminated. */
303 for (const char *p = envval; *p != '\0'; p++, envvallen++);
304
305 tunable_initialize (cur, envval, envvallen);
306 break;
307 }
308 }
309 }
310}
311
312void
313__tunables_print (void)
314{
315 for (int i = 0; i < array_length (tunable_list); i++)
316 {
317 const tunable_t *cur = &tunable_list[i];
318 if (cur->type.type_code == TUNABLE_TYPE_STRING
319 && cur->val.strval.str == NULL)
320 _dl_printf ("%s:\n", cur->name);
321 else
322 {
323 _dl_printf ("%s: ", cur->name);
324 switch (cur->type.type_code)
325 {
326 case TUNABLE_TYPE_INT_32:
327 _dl_printf ("%d (min: %d, max: %d)\n",
328 (int) cur->val.numval,
329 (int) cur->type.min,
330 (int) cur->type.max);
331 break;
332 case TUNABLE_TYPE_UINT_64:
333 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
334 (long int) cur->val.numval,
335 (long int) cur->type.min,
336 (long int) cur->type.max);
337 break;
338 case TUNABLE_TYPE_SIZE_T:
339 _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
340 (size_t) cur->val.numval,
341 (size_t) cur->type.min,
342 (size_t) cur->type.max);
343 break;
344 case TUNABLE_TYPE_STRING:
345 _dl_printf ("%.*s\n",
346 (int) cur->val.strval.len,
347 cur->val.strval.str);
348 break;
349 default:
350 __builtin_unreachable ();
351 }
352 }
353 }
354}
355
356void
357__tunable_get_default (tunable_id_t id, void *valp)
358{
359 tunable_t *cur = &tunable_list[id];
360
361 switch (cur->type.type_code)
362 {
363 case TUNABLE_TYPE_UINT_64:
364 {
365 *((uint64_t *) valp) = (uint64_t) cur->def.numval;
366 break;
367 }
368 case TUNABLE_TYPE_INT_32:
369 {
370 *((int32_t *) valp) = (int32_t) cur->def.numval;
371 break;
372 }
373 case TUNABLE_TYPE_SIZE_T:
374 {
375 *((size_t *) valp) = (size_t) cur->def.numval;
376 break;
377 }
378 case TUNABLE_TYPE_STRING:
379 {
380 *((const struct tunable_str_t **)valp) = &cur->def.strval;
381 break;
382 }
383 default:
384 __builtin_unreachable ();
385 }
386}
387rtld_hidden_def (__tunable_get_default)
388
389/* Set the tunable value. This is called by the module that the tunable exists
390 in. */
391void
392__tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
393{
394 tunable_t *cur = &tunable_list[id];
395
396 switch (cur->type.type_code)
397 {
398 case TUNABLE_TYPE_UINT_64:
399 {
400 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
401 break;
402 }
403 case TUNABLE_TYPE_INT_32:
404 {
405 *((int32_t *) valp) = (int32_t) cur->val.numval;
406 break;
407 }
408 case TUNABLE_TYPE_SIZE_T:
409 {
410 *((size_t *) valp) = (size_t) cur->val.numval;
411 break;
412 }
413 case TUNABLE_TYPE_STRING:
414 {
415 *((const struct tunable_str_t **) valp) = &cur->val.strval;
416 break;
417 }
418 default:
419 __builtin_unreachable ();
420 }
421
422 if (cur->initialized && callback != NULL)
423 callback (&cur->val);
424}
425
426rtld_hidden_def (__tunable_get_val)
427

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of glibc/elf/dl-tunables.c