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