327b9eb52f0942cc197c1c13e0d6c5dc9700eddb
[platform/upstream/glibc.git] / elf / dl-tunables.c
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-2023 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 #include <not-errno.h>
40
41 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
42 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
43 #endif
44
45 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
46 static char *
47 tunables_strdup (const char *in)
48 {
49   size_t i = 0;
50
51   while (in[i++] != '\0');
52   char *out = __minimal_malloc (i + 1);
53
54   /* For most of the tunables code, we ignore user errors.  However,
55      this is a system error - and running out of memory at program
56      startup should be reported, so we do.  */
57   if (out == NULL)
58     _dl_fatal_printf ("failed to allocate memory to process tunables\n");
59
60   while (i-- > 0)
61     out[i] = in[i];
62
63   return out;
64 }
65 #endif
66
67 static char **
68 get_next_env (char **envp, char **name, size_t *namelen, char **val,
69               char ***prev_envp)
70 {
71   while (envp != NULL && *envp != NULL)
72     {
73       char **prev = envp;
74       char *envline = *envp++;
75       int len = 0;
76
77       while (envline[len] != '\0' && envline[len] != '=')
78         len++;
79
80       /* Just the name and no value, go to the next one.  */
81       if (envline[len] == '\0')
82         continue;
83
84       *name = envline;
85       *namelen = len;
86       *val = &envline[len + 1];
87       *prev_envp = prev;
88
89       return envp;
90     }
91
92   return NULL;
93 }
94
95 static void
96 do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
97                        const tunable_num_t *minp,
98                        const tunable_num_t *maxp)
99 {
100   tunable_num_t val, min, max;
101
102   if (cur->type.type_code == TUNABLE_TYPE_STRING)
103     {
104       cur->val.strval = valp->strval;
105       cur->initialized = true;
106       return;
107     }
108
109   bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
110
111   val = valp->numval;
112   min = minp != NULL ? *minp : cur->type.min;
113   max = maxp != NULL ? *maxp : cur->type.max;
114
115   /* We allow only increasingly restrictive bounds.  */
116   if (tunable_val_lt (min, cur->type.min, unsigned_cmp))
117     min = cur->type.min;
118
119   if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
120     max = cur->type.max;
121
122   /* Skip both bounds if they're inconsistent.  */
123   if (tunable_val_gt (min, max, unsigned_cmp))
124     {
125       min = cur->type.min;
126       max = cur->type.max;
127     }
128
129   /* Bail out if the bounds are not valid.  */
130   if (tunable_val_lt (val, min, unsigned_cmp)
131       || tunable_val_lt (max, val, unsigned_cmp))
132     return;
133
134   cur->val.numval = val;
135   cur->type.min = min;
136   cur->type.max = max;
137   cur->initialized = true;
138 }
139
140 /* Validate range of the input value and initialize the tunable CUR if it looks
141    good.  */
142 static void
143 tunable_initialize (tunable_t *cur, const char *strval)
144 {
145   tunable_val_t val;
146
147   if (cur->type.type_code != TUNABLE_TYPE_STRING)
148     val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
149   else
150     val.strval = strval;
151   do_tunable_update_val (cur, &val, NULL, NULL);
152 }
153
154 void
155 __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
156                    tunable_num_t *maxp)
157 {
158   tunable_t *cur = &tunable_list[id];
159
160   do_tunable_update_val (cur, valp, minp, maxp);
161 }
162
163 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
164 /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
165    be unsafe for AT_SECURE processes so that it can be used as the new
166    environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
167    environment variable string which we use to make NULL terminated values so
168    that we don't have to allocate memory again for it.  */
169 static void
170 parse_tunables (char *tunestr, char *valstring)
171 {
172   if (tunestr == NULL || *tunestr == '\0')
173     return;
174
175   char *p = tunestr;
176   size_t off = 0;
177
178   while (true)
179     {
180       char *name = p;
181       size_t len = 0;
182
183       /* First, find where the name ends.  */
184       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
185         len++;
186
187       /* If we reach the end of the string before getting a valid name-value
188          pair, bail out.  */
189       if (p[len] == '\0')
190         {
191           if (__libc_enable_secure)
192             tunestr[off] = '\0';
193           return;
194         }
195
196       /* We did not find a valid name-value pair before encountering the
197          colon.  */
198       if (p[len]== ':')
199         {
200           p += len + 1;
201           continue;
202         }
203
204       p += len + 1;
205
206       /* Take the value from the valstring since we need to NULL terminate it.  */
207       char *value = &valstring[p - tunestr];
208       len = 0;
209
210       while (p[len] != ':' && p[len] != '\0')
211         len++;
212
213       /* Add the tunable if it exists.  */
214       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
215         {
216           tunable_t *cur = &tunable_list[i];
217
218           if (tunable_is_name (cur->name, name))
219             {
220               /* If we are in a secure context (AT_SECURE) then ignore the
221                  tunable unless it is explicitly marked as secure.  Tunable
222                  values take precedence over their envvar aliases.  We write
223                  the tunables that are not SXID_ERASE back to TUNESTR, thus
224                  dropping all SXID_ERASE tunables and any invalid or
225                  unrecognized tunables.  */
226               if (__libc_enable_secure)
227                 {
228                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
229                     {
230                       if (off > 0)
231                         tunestr[off++] = ':';
232
233                       const char *n = cur->name;
234
235                       while (*n != '\0')
236                         tunestr[off++] = *n++;
237
238                       tunestr[off++] = '=';
239
240                       for (size_t j = 0; j < len; j++)
241                         tunestr[off++] = value[j];
242                     }
243
244                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
245                     break;
246                 }
247
248               value[len] = '\0';
249               tunable_initialize (cur, value);
250               break;
251             }
252         }
253
254       if (p[len] != '\0')
255         p += len + 1;
256     }
257 }
258 #endif
259
260 /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
261    the system administrator has created the /etc/suid-debug file.  This is a
262    special case where we want to conditionally enable/disable a tunable even
263    for setuid binaries.  We use the special version of access() to avoid
264    setting ERRNO, which is a TLS variable since TLS has not yet been set
265    up.  */
266 static __always_inline void
267 maybe_enable_malloc_check (void)
268 {
269   tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
270   if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
271     tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
272 }
273
274 /* Initialize the tunables list from the environment.  For now we only use the
275    ENV_ALIAS to find values.  Later we will also use the tunable names to find
276    values.  */
277 void
278 __tunables_init (char **envp)
279 {
280   char *envname = NULL;
281   char *envval = NULL;
282   size_t len = 0;
283   char **prev_envp = envp;
284
285   maybe_enable_malloc_check ();
286
287   while ((envp = get_next_env (envp, &envname, &len, &envval,
288                                &prev_envp)) != NULL)
289     {
290 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
291       if (tunable_is_name (GLIBC_TUNABLES, envname))
292         {
293           char *new_env = tunables_strdup (envname);
294           if (new_env != NULL)
295             parse_tunables (new_env + len + 1, envval);
296           /* Put in the updated envval.  */
297           *prev_envp = new_env;
298           continue;
299         }
300 #endif
301
302       for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
303         {
304           tunable_t *cur = &tunable_list[i];
305
306           /* Skip over tunables that have either been set already or should be
307              skipped.  */
308           if (cur->initialized || cur->env_alias[0] == '\0')
309             continue;
310
311           const char *name = cur->env_alias;
312
313           /* We have a match.  Initialize and move on to the next line.  */
314           if (tunable_is_name (name, envname))
315             {
316               /* For AT_SECURE binaries, we need to check the security settings of
317                  the tunable and decide whether we read the value and also whether
318                  we erase the value so that child processes don't inherit them in
319                  the environment.  */
320               if (__libc_enable_secure)
321                 {
322                   if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
323                     {
324                       /* Erase the environment variable.  */
325                       char **ep = prev_envp;
326
327                       while (*ep != NULL)
328                         {
329                           if (tunable_is_name (name, *ep))
330                             {
331                               char **dp = ep;
332
333                               do
334                                 dp[0] = dp[1];
335                               while (*dp++);
336                             }
337                           else
338                             ++ep;
339                         }
340                       /* Reset the iterator so that we read the environment again
341                          from the point we erased.  */
342                       envp = prev_envp;
343                     }
344
345                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
346                     continue;
347                 }
348
349               tunable_initialize (cur, envval);
350               break;
351             }
352         }
353     }
354 }
355
356 void
357 __tunables_print (void)
358 {
359   for (int i = 0; i < array_length (tunable_list); i++)
360     {
361       const tunable_t *cur = &tunable_list[i];
362       if (cur->type.type_code == TUNABLE_TYPE_STRING
363           && cur->val.strval == NULL)
364         _dl_printf ("%s:\n", cur->name);
365       else
366         {
367           _dl_printf ("%s: ", cur->name);
368           switch (cur->type.type_code)
369             {
370             case TUNABLE_TYPE_INT_32:
371               _dl_printf ("%d (min: %d, max: %d)\n",
372                           (int) cur->val.numval,
373                           (int) cur->type.min,
374                           (int) cur->type.max);
375               break;
376             case TUNABLE_TYPE_UINT_64:
377               _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
378                           (long int) cur->val.numval,
379                           (long int) cur->type.min,
380                           (long int) cur->type.max);
381               break;
382             case TUNABLE_TYPE_SIZE_T:
383               _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
384                           (size_t) cur->val.numval,
385                           (size_t) cur->type.min,
386                           (size_t) cur->type.max);
387               break;
388             case TUNABLE_TYPE_STRING:
389               _dl_printf ("%s\n", cur->val.strval);
390               break;
391             default:
392               __builtin_unreachable ();
393             }
394         }
395     }
396 }
397
398 /* Set the tunable value.  This is called by the module that the tunable exists
399    in. */
400 void
401 __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
402 {
403   tunable_t *cur = &tunable_list[id];
404
405   switch (cur->type.type_code)
406     {
407     case TUNABLE_TYPE_UINT_64:
408         {
409           *((uint64_t *) valp) = (uint64_t) cur->val.numval;
410           break;
411         }
412     case TUNABLE_TYPE_INT_32:
413         {
414           *((int32_t *) valp) = (int32_t) cur->val.numval;
415           break;
416         }
417     case TUNABLE_TYPE_SIZE_T:
418         {
419           *((size_t *) valp) = (size_t) cur->val.numval;
420           break;
421         }
422     case TUNABLE_TYPE_STRING:
423         {
424           *((const char **)valp) = cur->val.strval;
425           break;
426         }
427     default:
428       __builtin_unreachable ();
429     }
430
431   if (cur->initialized && callback != NULL)
432     callback (&cur->val);
433 }
434
435 rtld_hidden_def (__tunable_get_val)