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-2017 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 <http://www.gnu.org/licenses/>. */
20
21#include <stdint.h>
22#include <stdbool.h>
23#include <unistd.h>
24#include <stdlib.h>
25#include <sysdep.h>
26#include <fcntl.h>
27#include <ldsodefs.h>
28
29#define TUNABLES_INTERNAL 1
30#include "dl-tunables.h"
31
32#include <not-errno.h>
33
34#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
35# define GLIBC_TUNABLES "GLIBC_TUNABLES"
36#endif
37
38#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
39static char *
40tunables_strdup (const char *in)
41{
42 size_t i = 0;
43
44 while (in[i++] != '\0');
45 char *out = __sbrk (i);
46
47 /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
48 set the thread-local errno since the TCB has not yet been set up. This
49 needs to be fixed with an __sbrk implementation that does not set
50 errno. */
51 if (out == (void *)-1)
52 return NULL;
53
54 i--;
55
56 while (i-- > 0)
57 out[i] = in[i];
58
59 return out;
60}
61#endif
62
63static char **
64get_next_env (char **envp, char **name, size_t *namelen, char **val,
65 char ***prev_envp)
66{
67 while (envp != NULL && *envp != NULL)
68 {
69 char **prev = envp;
70 char *envline = *envp++;
71 int len = 0;
72
73 while (envline[len] != '\0' && envline[len] != '=')
74 len++;
75
76 /* Just the name and no value, go to the next one. */
77 if (envline[len] == '\0')
78 continue;
79
80 *name = envline;
81 *namelen = len;
82 *val = &envline[len + 1];
83 *prev_envp = prev;
84
85 return envp;
86 }
87
88 return NULL;
89}
90
91#define TUNABLE_SET_VAL_IF_VALID_RANGE(__cur, __val, __type) \
92({ \
93 __type min = (__cur)->type.min; \
94 __type max = (__cur)->type.max; \
95 \
96 if ((__type) (__val) >= min && (__type) (val) <= max) \
97 { \
98 (__cur)->val.numval = val; \
99 (__cur)->initialized = true; \
100 } \
101})
102
103static void
104do_tunable_update_val (tunable_t *cur, const void *valp)
105{
106 uint64_t val;
107
108 if (cur->type.type_code != TUNABLE_TYPE_STRING)
109 val = *((int64_t *) valp);
110
111 switch (cur->type.type_code)
112 {
113 case TUNABLE_TYPE_INT_32:
114 {
115 TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, int64_t);
116 break;
117 }
118 case TUNABLE_TYPE_UINT_64:
119 {
120 TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t);
121 break;
122 }
123 case TUNABLE_TYPE_SIZE_T:
124 {
125 TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t);
126 break;
127 }
128 case TUNABLE_TYPE_STRING:
129 {
130 cur->val.strval = valp;
131 break;
132 }
133 default:
134 __builtin_unreachable ();
135 }
136}
137
138/* Validate range of the input value and initialize the tunable CUR if it looks
139 good. */
140static void
141tunable_initialize (tunable_t *cur, const char *strval)
142{
143 uint64_t val;
144 const void *valp;
145
146 if (cur->type.type_code != TUNABLE_TYPE_STRING)
147 {
148 val = _dl_strtoul (strval, NULL);
149 valp = &val;
150 }
151 else
152 {
153 cur->initialized = true;
154 valp = strval;
155 }
156 do_tunable_update_val (cur, valp);
157}
158
159void
160__tunable_set_val (tunable_id_t id, void *valp)
161{
162 tunable_t *cur = &tunable_list[id];
163
164 do_tunable_update_val (cur, valp);
165}
166
167#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
168/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
169 be unsafe for AT_SECURE processes so that it can be used as the new
170 environment variable value for GLIBC_TUNABLES. VALSTRING is the original
171 environment variable string which we use to make NULL terminated values so
172 that we don't have to allocate memory again for it. */
173static void
174parse_tunables (char *tunestr, char *valstring)
175{
176 if (tunestr == NULL || *tunestr == '\0')
177 return;
178
179 char *p = tunestr;
180
181 while (true)
182 {
183 char *name = p;
184 size_t len = 0;
185
186 /* First, find where the name ends. */
187 while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
188 len++;
189
190 /* If we reach the end of the string before getting a valid name-value
191 pair, bail out. */
192 if (p[len] == '\0')
193 return;
194
195 /* We did not find a valid name-value pair before encountering the
196 colon. */
197 if (p[len]== ':')
198 {
199 p += len + 1;
200 continue;
201 }
202
203 p += len + 1;
204
205 /* Take the value from the valstring since we need to NULL terminate it. */
206 char *value = &valstring[p - tunestr];
207 len = 0;
208
209 while (p[len] != ':' && p[len] != '\0')
210 len++;
211
212 /* Add the tunable if it exists. */
213 for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
214 {
215 tunable_t *cur = &tunable_list[i];
216
217 if (tunable_is_name (cur->name, name))
218 {
219 /* If we are in a secure context (AT_SECURE) then ignore the tunable
220 unless it is explicitly marked as secure. Tunable values take
221 precendence over their envvar aliases. */
222 if (__libc_enable_secure)
223 {
224 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
225 {
226 if (p[len] == '\0')
227 {
228 /* Last tunable in the valstring. Null-terminate and
229 return. */
230 *name = '\0';
231 return;
232 }
233 else
234 {
235 /* Remove the current tunable from the string. We do
236 this by overwriting the string starting from NAME
237 (which is where the current tunable begins) with
238 the remainder of the string. We then have P point
239 to NAME so that we continue in the correct
240 position in the valstring. */
241 char *q = &p[len + 1];
242 p = name;
243 while (*q != '\0')
244 *name++ = *q++;
245 name[0] = '\0';
246 len = 0;
247 }
248 }
249
250 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
251 break;
252 }
253
254 value[len] = '\0';
255 tunable_initialize (cur, value);
256 break;
257 }
258 }
259
260 if (p[len] == '\0')
261 return;
262 else
263 p += len + 1;
264 }
265}
266#endif
267
268/* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
269 the system administrator has created the /etc/suid-debug file. This is a
270 special case where we want to conditionally enable/disable a tunable even
271 for setuid binaries. We use the special version of access() to avoid
272 setting ERRNO, which is a TLS variable since TLS has not yet been set
273 up. */
274static inline void
275__always_inline
276maybe_enable_malloc_check (void)
277{
278 tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
279 if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
280 tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
281}
282
283/* Initialize the tunables list from the environment. For now we only use the
284 ENV_ALIAS to find values. Later we will also use the tunable names to find
285 values. */
286void
287__tunables_init (char **envp)
288{
289 char *envname = NULL;
290 char *envval = NULL;
291 size_t len = 0;
292 char **prev_envp = envp;
293
294 maybe_enable_malloc_check ();
295
296 while ((envp = get_next_env (envp, &envname, &len, &envval,
297 &prev_envp)) != NULL)
298 {
299#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
300 if (tunable_is_name (GLIBC_TUNABLES, envname))
301 {
302 char *new_env = tunables_strdup (envname);
303 if (new_env != NULL)
304 parse_tunables (new_env + len + 1, envval);
305 /* Put in the updated envval. */
306 *prev_envp = new_env;
307 continue;
308 }
309#endif
310
311 for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
312 {
313 tunable_t *cur = &tunable_list[i];
314
315 /* Skip over tunables that have either been set already or should be
316 skipped. */
317 if (cur->initialized || cur->env_alias == NULL)
318 continue;
319
320 const char *name = cur->env_alias;
321
322 /* We have a match. Initialize and move on to the next line. */
323 if (tunable_is_name (name, envname))
324 {
325 /* For AT_SECURE binaries, we need to check the security settings of
326 the tunable and decide whether we read the value and also whether
327 we erase the value so that child processes don't inherit them in
328 the environment. */
329 if (__libc_enable_secure)
330 {
331 if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
332 {
333 /* Erase the environment variable. */
334 char **ep = prev_envp;
335
336 while (*ep != NULL)
337 {
338 if (tunable_is_name (name, *ep))
339 {
340 char **dp = ep;
341
342 do
343 dp[0] = dp[1];
344 while (*dp++);
345 }
346 else
347 ++ep;
348 }
349 /* Reset the iterator so that we read the environment again
350 from the point we erased. */
351 envp = prev_envp;
352 }
353
354 if (cur->security_level != TUNABLE_SECLEVEL_NONE)
355 continue;
356 }
357
358 tunable_initialize (cur, envval);
359 break;
360 }
361 }
362 }
363}
364
365/* Set the tunable value. This is called by the module that the tunable exists
366 in. */
367void
368__tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
369{
370 tunable_t *cur = &tunable_list[id];
371
372 switch (cur->type.type_code)
373 {
374 case TUNABLE_TYPE_UINT_64:
375 {
376 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
377 break;
378 }
379 case TUNABLE_TYPE_INT_32:
380 {
381 *((int32_t *) valp) = (int32_t) cur->val.numval;
382 break;
383 }
384 case TUNABLE_TYPE_SIZE_T:
385 {
386 *((size_t *) valp) = (size_t) cur->val.numval;
387 break;
388 }
389 case TUNABLE_TYPE_STRING:
390 {
391 *((const char **)valp) = cur->val.strval;
392 break;
393 }
394 default:
395 __builtin_unreachable ();
396 }
397
398 if (cur->initialized && callback != NULL)
399 callback (&cur->val);
400}
401
402rtld_hidden_def (__tunable_get_val)
403