Bug Summary

File:build-scan/../src/basic/env-util.c
Warning:line 223, column 12
Use of zero-allocated memory

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name env-util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/basic/libbasic.a.p -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/blkid -I /usr/include/libmount -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/basic/env-util.c

../src/basic/env-util.c

1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <errno(*__errno_location ()).h>
4#include <limits.h>
5#include <stdarg.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#include "alloc-util.h"
11#include "env-util.h"
12#include "escape.h"
13#include "extract-word.h"
14#include "macro.h"
15#include "parse-util.h"
16#include "string-util.h"
17#include "strv.h"
18#include "utf8.h"
19
20#define VALID_CHARS_ENV_NAME"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"_"
\
21 DIGITS"0123456789" LETTERS"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
22 "_"
23
24#ifndef ARG_MAX((size_t) sysconf(_SC_ARG_MAX))
25#define ARG_MAX((size_t) sysconf(_SC_ARG_MAX)) ((size_t) sysconf(_SC_ARG_MAX_SC_ARG_MAX))
26#endif
27
28static bool_Bool env_name_is_valid_n(const char *e, size_t n) {
29 const char *p;
30
31 if (!e)
32 return false0;
33
34 if (n <= 0)
35 return false0;
36
37 if (e[0] >= '0' && e[0] <= '9')
38 return false0;
39
40 /* POSIX says the overall size of the environment block cannot
41 * be > ARG_MAX, an individual assignment hence cannot be
42 * either. Discounting the equal sign and trailing NUL this
43 * hence leaves ARG_MAX-2 as longest possible variable
44 * name. */
45 if (n > ARG_MAX((size_t) sysconf(_SC_ARG_MAX)) - 2)
46 return false0;
47
48 for (p = e; p < e + n; p++)
49 if (!strchr(VALID_CHARS_ENV_NAME"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"_"
, *p))
50 return false0;
51
52 return true1;
53}
54
55bool_Bool env_name_is_valid(const char *e) {
56 if (!e)
57 return false0;
58
59 return env_name_is_valid_n(e, strlen(e));
60}
61
62bool_Bool env_value_is_valid(const char *e) {
63 if (!e)
64 return false0;
65
66 if (!utf8_is_valid(e))
67 return false0;
68
69 /* bash allows tabs and newlines in environment variables, and so
70 * should we */
71 if (string_has_cc(e, "\t\n"))
72 return false0;
73
74 /* POSIX says the overall size of the environment block cannot
75 * be > ARG_MAX, an individual assignment hence cannot be
76 * either. Discounting the shortest possible variable name of
77 * length 1, the equal sign and trailing NUL this hence leaves
78 * ARG_MAX-3 as longest possible variable value. */
79 if (strlen(e) > ARG_MAX((size_t) sysconf(_SC_ARG_MAX)) - 3)
80 return false0;
81
82 return true1;
83}
84
85bool_Bool env_assignment_is_valid(const char *e) {
86 const char *eq;
87
88 eq = strchr(e, '=');
89 if (!eq)
90 return false0;
91
92 if (!env_name_is_valid_n(e, eq - e))
93 return false0;
94
95 if (!env_value_is_valid(eq + 1))
96 return false0;
97
98 /* POSIX says the overall size of the environment block cannot
99 * be > ARG_MAX, hence the individual variable assignments
100 * cannot be either, but let's leave room for one trailing NUL
101 * byte. */
102 if (strlen(e) > ARG_MAX((size_t) sysconf(_SC_ARG_MAX)) - 1)
103 return false0;
104
105 return true1;
106}
107
108bool_Bool strv_env_is_valid(char **e) {
109 char **p, **q;
110
111 STRV_FOREACH(p, e)for ((p) = (e); (p) && *(p); (p)++) {
112 size_t k;
113
114 if (!env_assignment_is_valid(*p))
115 return false0;
116
117 /* Check if there are duplicate assginments */
118 k = strcspn(*p, "=");
119 STRV_FOREACH(q, p + 1)for ((q) = (p + 1); (q) && *(q); (q)++)
120 if (strneq(*p, *q, k)(strncmp((*p), (*q), (k)) == 0) && (*q)[k] == '=')
121 return false0;
122 }
123
124 return true1;
125}
126
127bool_Bool strv_env_name_is_valid(char **l) {
128 char **p, **q;
129
130 STRV_FOREACH(p, l)for ((p) = (l); (p) && *(p); (p)++) {
131 if (!env_name_is_valid(*p))
132 return false0;
133
134 STRV_FOREACH(q, p + 1)for ((q) = (p + 1); (q) && *(q); (q)++)
135 if (streq(*p, *q)(strcmp((*p),(*q)) == 0))
136 return false0;
137 }
138
139 return true1;
140}
141
142bool_Bool strv_env_name_or_assignment_is_valid(char **l) {
143 char **p, **q;
144
145 STRV_FOREACH(p, l)for ((p) = (l); (p) && *(p); (p)++) {
146 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
147 return false0;
148
149 STRV_FOREACH(q, p + 1)for ((q) = (p + 1); (q) && *(q); (q)++)
150 if (streq(*p, *q)(strcmp((*p),(*q)) == 0))
151 return false0;
152 }
153
154 return true1;
155}
156
157static int env_append(char **r, char ***k, char **a) {
158 assert(r)do { if ((__builtin_expect(!!(!(r)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("r"), "../src/basic/env-util.c", 158, __PRETTY_FUNCTION__
); } while (0)
;
159 assert(k)do { if ((__builtin_expect(!!(!(k)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("k"), "../src/basic/env-util.c", 159, __PRETTY_FUNCTION__
); } while (0)
;
160
161 if (!a)
162 return 0;
163
164 /* Add the entries of a to *k unless they already exist in *r
165 * in which case they are overridden instead. This assumes
166 * there is enough space in the r array. */
167
168 for (; *a; a++) {
169 char **j;
170 size_t n;
171
172 n = strcspn(*a, "=");
173
174 if ((*a)[n] == '=')
175 n++;
176
177 for (j = r; j < *k; j++)
178 if (strneq(*j, *a, n)(strncmp((*j), (*a), (n)) == 0))
179 break;
180
181 if (j >= *k)
182 (*k)++;
183 else
184 free(*j);
185
186 *j = strdup(*a);
187 if (!*j)
188 return -ENOMEM12;
189 }
190
191 return 0;
192}
193
194char **strv_env_merge(size_t n_lists, ...) {
195 size_t n = 0;
196 char **l, **k, **r;
197 va_list ap;
198 size_t i;
199
200 /* Merges an arbitrary number of environment sets */
201
202 va_start(ap, n_lists)__builtin_va_start(ap, n_lists);
203 for (i = 0; i < n_lists; i++) {
1
Assuming 'i' is < 'n_lists'
2
Loop condition is true. Entering loop body
3
Assuming 'i' is >= 'n_lists'
4
Loop condition is false. Execution continues on line 207
204 l = va_arg(ap, char**)__builtin_va_arg(ap, char**);
205 n += strv_length(l);
206 }
207 va_end(ap)__builtin_va_end(ap);
208
209 r = new(char*, n+1)((char**) malloc_multiply(sizeof(char*), (n+1)));
5
Calling 'malloc_multiply'
8
Returned allocated memory
210 if (!r)
9
Assuming 'r' is non-null
10
Taking false branch
211 return NULL((void*)0);
212
213 k = r;
214
215 va_start(ap, n_lists)__builtin_va_start(ap, n_lists);
216 for (i = 0; i < n_lists; i++) {
11
Assuming 'i' is >= 'n_lists'
12
Loop condition is false. Execution continues on line 221
217 l = va_arg(ap, char**)__builtin_va_arg(ap, char**);
218 if (env_append(r, &k, l) < 0)
219 goto fail;
220 }
221 va_end(ap)__builtin_va_end(ap);
222
223 *k = NULL((void*)0);
13
Use of zero-allocated memory
224
225 return r;
226
227fail:
228 va_end(ap)__builtin_va_end(ap);
229 strv_free(r);
230
231 return NULL((void*)0);
232}
233
234static bool_Bool env_match(const char *t, const char *pattern) {
235 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/env-util.c", 235, __PRETTY_FUNCTION__
); } while (0)
;
236 assert(pattern)do { if ((__builtin_expect(!!(!(pattern)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pattern"), "../src/basic/env-util.c", 236
, __PRETTY_FUNCTION__); } while (0)
;
237
238 /* pattern a matches string a
239 * a matches a=
240 * a matches a=b
241 * a= matches a=
242 * a=b matches a=b
243 * a= does not match a
244 * a=b does not match a=
245 * a=b does not match a
246 * a=b does not match a=c */
247
248 if (streq(t, pattern)(strcmp((t),(pattern)) == 0))
249 return true1;
250
251 if (!strchr(pattern, '=')) {
252 size_t l = strlen(pattern);
253
254 return strneq(t, pattern, l)(strncmp((t), (pattern), (l)) == 0) && t[l] == '=';
255 }
256
257 return false0;
258}
259
260static bool_Bool env_entry_has_name(const char *entry, const char *name) {
261 const char *t;
262
263 assert(entry)do { if ((__builtin_expect(!!(!(entry)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("entry"), "../src/basic/env-util.c", 263
, __PRETTY_FUNCTION__); } while (0)
;
264 assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("name"), "../src/basic/env-util.c", 264,
__PRETTY_FUNCTION__); } while (0)
;
265
266 t = startswith(entry, name);
267 if (!t)
268 return false0;
269
270 return *t == '=';
271}
272
273char **strv_env_delete(char **x, size_t n_lists, ...) {
274 size_t n, i = 0;
275 char **k, **r;
276 va_list ap;
277
278 /* Deletes every entry from x that is mentioned in the other
279 * string lists */
280
281 n = strv_length(x);
282
283 r = new(char*, n+1)((char**) malloc_multiply(sizeof(char*), (n+1)));
284 if (!r)
285 return NULL((void*)0);
286
287 STRV_FOREACH(k, x)for ((k) = (x); (k) && *(k); (k)++) {
288 size_t v;
289
290 va_start(ap, n_lists)__builtin_va_start(ap, n_lists);
291 for (v = 0; v < n_lists; v++) {
292 char **l, **j;
293
294 l = va_arg(ap, char**)__builtin_va_arg(ap, char**);
295 STRV_FOREACH(j, l)for ((j) = (l); (j) && *(j); (j)++)
296 if (env_match(*k, *j))
297 goto skip;
298 }
299 va_end(ap)__builtin_va_end(ap);
300
301 r[i] = strdup(*k);
302 if (!r[i]) {
303 strv_free(r);
304 return NULL((void*)0);
305 }
306
307 i++;
308 continue;
309
310 skip:
311 va_end(ap)__builtin_va_end(ap);
312 }
313
314 r[i] = NULL((void*)0);
315
316 assert(i <= n)do { if ((__builtin_expect(!!(!(i <= n)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("i <= n"), "../src/basic/env-util.c",
316, __PRETTY_FUNCTION__); } while (0)
;
317
318 return r;
319}
320
321char **strv_env_unset(char **l, const char *p) {
322
323 char **f, **t;
324
325 if (!l)
326 return NULL((void*)0);
327
328 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/env-util.c", 328, __PRETTY_FUNCTION__
); } while (0)
;
329
330 /* Drops every occurrence of the env var setting p in the
331 * string list. Edits in-place. */
332
333 for (f = t = l; *f; f++) {
334
335 if (env_match(*f, p)) {
336 free(*f);
337 continue;
338 }
339
340 *(t++) = *f;
341 }
342
343 *t = NULL((void*)0);
344 return l;
345}
346
347char **strv_env_unset_many(char **l, ...) {
348
349 char **f, **t;
350
351 if (!l)
352 return NULL((void*)0);
353
354 /* Like strv_env_unset() but applies many at once. Edits in-place. */
355
356 for (f = t = l; *f; f++) {
357 bool_Bool found = false0;
358 const char *p;
359 va_list ap;
360
361 va_start(ap, l)__builtin_va_start(ap, l);
362
363 while ((p = va_arg(ap, const char*)__builtin_va_arg(ap, const char*))) {
364 if (env_match(*f, p)) {
365 found = true1;
366 break;
367 }
368 }
369
370 va_end(ap)__builtin_va_end(ap);
371
372 if (found) {
373 free(*f);
374 continue;
375 }
376
377 *(t++) = *f;
378 }
379
380 *t = NULL((void*)0);
381 return l;
382}
383
384int strv_env_replace(char ***l, char *p) {
385 char **f;
386 const char *t, *name;
387
388 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/env-util.c", 388, __PRETTY_FUNCTION__
); } while (0)
;
389
390 /* Replace first occurrence of the env var or add a new one in the
391 * string list. Drop other occurences. Edits in-place. Does not copy p.
392 * p must be a valid key=value assignment.
393 */
394
395 t = strchr(p, '=');
396 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/env-util.c", 396, __PRETTY_FUNCTION__
); } while (0)
;
397
398 name = strndupa(p, t - p)(__extension__ ({ const char *__old = (p); size_t __len = strnlen
(__old, (t - p)); char *__new = (char *) __builtin_alloca (__len
+ 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len
); }))
;
399
400 for (f = *l; f && *f; f++)
401 if (env_entry_has_name(*f, name)) {
402 free_and_replace(*f, p)({ free(*f); (*f) = (p); (p) = ((void*)0); 0; });
403 strv_env_unset(f + 1, *f);
404 return 0;
405 }
406
407 /* We didn't find a match, we need to append p or create a new strv */
408 if (strv_push(l, p) < 0)
409 return -ENOMEM12;
410 return 1;
411}
412
413char **strv_env_set(char **x, const char *p) {
414
415 char **k;
416 _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **r = NULL((void*)0);
417 char* m[2] = { (char*) p, NULL((void*)0) };
418
419 /* Overrides the env var setting of p, returns a new copy */
420
421 r = new(char*, strv_length(x)+2)((char**) malloc_multiply(sizeof(char*), (strv_length(x)+2)));
422 if (!r)
423 return NULL((void*)0);
424
425 k = r;
426 if (env_append(r, &k, x) < 0)
427 return NULL((void*)0);
428
429 if (env_append(r, &k, m) < 0)
430 return NULL((void*)0);
431
432 *k = NULL((void*)0);
433
434 return TAKE_PTR(r)({ typeof(r) _ptr_ = (r); (r) = ((void*)0); _ptr_; });
435}
436
437char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
438 char **i;
439
440 assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("name"), "../src/basic/env-util.c", 440,
__PRETTY_FUNCTION__); } while (0)
;
441
442 if (k <= 0)
443 return NULL((void*)0);
444
445 STRV_FOREACH_BACKWARDS(i, l)for (i = ({ char **_l = l; _l ? _l + strv_length(_l) - 1U : (
(void*)0); }); (l) && ((i) >= (l)); (i)--)
446 if (strneq(*i, name, k)(strncmp((*i), (name), (k)) == 0) &&
447 (*i)[k] == '=')
448 return *i + k + 1;
449
450 if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
451 const char *t;
452
453 t = strndupa(name, k)(__extension__ ({ const char *__old = (name); size_t __len = strnlen
(__old, (k)); char *__new = (char *) __builtin_alloca (__len
+ 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len
); }))
;
454 return getenv(t);
455 };
456
457 return NULL((void*)0);
458}
459
460char *strv_env_get(char **l, const char *name) {
461 assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("name"), "../src/basic/env-util.c", 461,
__PRETTY_FUNCTION__); } while (0)
;
462
463 return strv_env_get_n(l, name, strlen(name), 0);
464}
465
466char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
467 char **p, **q;
468 int k = 0;
469
470 STRV_FOREACH(p, e)for ((p) = (e); (p) && *(p); (p)++) {
471 size_t n;
472 bool_Bool duplicate = false0;
473
474 if (!env_assignment_is_valid(*p)) {
475 if (invalid_callback)
476 invalid_callback(*p, userdata);
477 free(*p);
478 continue;
479 }
480
481 n = strcspn(*p, "=");
482 STRV_FOREACH(q, p + 1)for ((q) = (p + 1); (q) && *(q); (q)++)
483 if (strneq(*p, *q, n)(strncmp((*p), (*q), (n)) == 0) && (*q)[n] == '=') {
484 duplicate = true1;
485 break;
486 }
487
488 if (duplicate) {
489 free(*p);
490 continue;
491 }
492
493 e[k++] = *p;
494 }
495
496 if (e)
497 e[k] = NULL((void*)0);
498
499 return e;
500}
501
502char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
503 enum {
504 WORD,
505 CURLY,
506 VARIABLE,
507 VARIABLE_RAW,
508 TEST,
509 DEFAULT_VALUE,
510 ALTERNATE_VALUE,
511 } state = WORD;
512
513 const char *e, *word = format, *test_value;
514 char *k;
515 _cleanup_free___attribute__((cleanup(freep))) char *r = NULL((void*)0);
516 size_t i, len;
517 int nest = 0;
518
519 assert(format)do { if ((__builtin_expect(!!(!(format)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("format"), "../src/basic/env-util.c", 519
, __PRETTY_FUNCTION__); } while (0)
;
520
521 for (e = format, i = 0; *e && i < n; e ++, i ++)
522 switch (state) {
523
524 case WORD:
525 if (*e == '$')
526 state = CURLY;
527 break;
528
529 case CURLY:
530 if (*e == '{') {
531 k = strnappend(r, word, e-word-1);
532 if (!k)
533 return NULL((void*)0);
534
535 free_and_replace(r, k)({ free(r); (r) = (k); (k) = ((void*)0); 0; });
536
537 word = e-1;
538 state = VARIABLE;
539 nest++;
540 } else if (*e == '$') {
541 k = strnappend(r, word, e-word);
542 if (!k)
543 return NULL((void*)0);
544
545 free_and_replace(r, k)({ free(r); (r) = (k); (k) = ((void*)0); 0; });
546
547 word = e+1;
548 state = WORD;
549
550 } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"_"
, *e)) {
551 k = strnappend(r, word, e-word-1);
552 if (!k)
553 return NULL((void*)0);
554
555 free_and_replace(r, k)({ free(r); (r) = (k); (k) = ((void*)0); 0; });
556
557 word = e-1;
558 state = VARIABLE_RAW;
559
560 } else
561 state = WORD;
562 break;
563
564 case VARIABLE:
565 if (*e == '}') {
566 const char *t;
567
568 t = strv_env_get_n(env, word+2, e-word-2, flags);
569
570 k = strappend(r, t);
571 if (!k)
572 return NULL((void*)0);
573
574 free_and_replace(r, k)({ free(r); (r) = (k); (k) = ((void*)0); 0; });
575
576 word = e+1;
577 state = WORD;
578 } else if (*e == ':') {
579 if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
580 /* Treat this as unsupported syntax, i.e. do no replacement */
581 state = WORD;
582 else {
583 len = e-word-2;
584 state = TEST;
585 }
586 }
587 break;
588
589 case TEST:
590 if (*e == '-')
591 state = DEFAULT_VALUE;
592 else if (*e == '+')
593 state = ALTERNATE_VALUE;
594 else {
595 state = WORD;
596 break;
597 }
598
599 test_value = e+1;
600 break;
601
602 case DEFAULT_VALUE: /* fall through */
603 case ALTERNATE_VALUE:
604 assert(flags & REPLACE_ENV_ALLOW_EXTENDED)do { if ((__builtin_expect(!!(!(flags & REPLACE_ENV_ALLOW_EXTENDED
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("flags & REPLACE_ENV_ALLOW_EXTENDED"
), "../src/basic/env-util.c", 604, __PRETTY_FUNCTION__); } while
(0)
;
605
606 if (*e == '{') {
607 nest++;
608 break;
609 }
610
611 if (*e != '}')
612 break;
613
614 nest--;
615 if (nest == 0) {
616 const char *t;
617 _cleanup_free___attribute__((cleanup(freep))) char *v = NULL((void*)0);
618
619 t = strv_env_get_n(env, word+2, len, flags);
620
621 if (t && state == ALTERNATE_VALUE)
622 t = v = replace_env_n(test_value, e-test_value, env, flags);
623 else if (!t && state == DEFAULT_VALUE)
624 t = v = replace_env_n(test_value, e-test_value, env, flags);
625
626 k = strappend(r, t);
627 if (!k)
628 return NULL((void*)0);
629
630 free_and_replace(r, k)({ free(r); (r) = (k); (k) = ((void*)0); 0; });
631
632 word = e+1;
633 state = WORD;
634 }
635 break;
636
637 case VARIABLE_RAW:
638 assert(flags & REPLACE_ENV_ALLOW_BRACELESS)do { if ((__builtin_expect(!!(!(flags & REPLACE_ENV_ALLOW_BRACELESS
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("flags & REPLACE_ENV_ALLOW_BRACELESS"
), "../src/basic/env-util.c", 638, __PRETTY_FUNCTION__); } while
(0)
;
639
640 if (!strchr(VALID_CHARS_ENV_NAME"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"_"
, *e)) {
641 const char *t;
642
643 t = strv_env_get_n(env, word+1, e-word-1, flags);
644
645 k = strappend(r, t);
646 if (!k)
647 return NULL((void*)0);
648
649 free_and_replace(r, k)({ free(r); (r) = (k); (k) = ((void*)0); 0; });
650
651 word = e--;
652 i--;
653 state = WORD;
654 }
655 break;
656 }
657
658 if (state == VARIABLE_RAW) {
659 const char *t;
660
661 assert(flags & REPLACE_ENV_ALLOW_BRACELESS)do { if ((__builtin_expect(!!(!(flags & REPLACE_ENV_ALLOW_BRACELESS
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("flags & REPLACE_ENV_ALLOW_BRACELESS"
), "../src/basic/env-util.c", 661, __PRETTY_FUNCTION__); } while
(0)
;
662
663 t = strv_env_get_n(env, word+1, e-word-1, flags);
664 return strappend(r, t);
665 } else
666 return strnappend(r, word, e-word);
667}
668
669char **replace_env_argv(char **argv, char **env) {
670 char **ret, **i;
671 size_t k = 0, l = 0;
672
673 l = strv_length(argv);
674
675 ret = new(char*, l+1)((char**) malloc_multiply(sizeof(char*), (l+1)));
676 if (!ret)
677 return NULL((void*)0);
678
679 STRV_FOREACH(i, argv)for ((i) = (argv); (i) && *(i); (i)++) {
680
681 /* If $FOO appears as single word, replace it by the split up variable */
682 if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){'{', '$'})/sizeof(int)]; switch((*i)[1])
{ case '{': case '$': _found = 1; break; default: break; } _found
; })
) {
683 char *e;
684 char **w, **m = NULL((void*)0);
685 size_t q;
686
687 e = strv_env_get(env, *i+1);
688 if (e) {
689 int r;
690
691 r = strv_split_extract(&m, e, WHITESPACE" \t\n\r", EXTRACT_RELAX|EXTRACT_QUOTES);
692 if (r < 0) {
693 ret[k] = NULL((void*)0);
694 strv_free(ret);
695 return NULL((void*)0);
696 }
697 } else
698 m = NULL((void*)0);
699
700 q = strv_length(m);
701 l = l + q - 1;
702
703 w = reallocarray(ret, l + 1, sizeof(char *));
704 if (!w) {
705 ret[k] = NULL((void*)0);
706 strv_free(ret);
707 strv_free(m);
708 return NULL((void*)0);
709 }
710
711 ret = w;
712 if (m) {
713 memcpy(ret + k, m, q * sizeof(char*));
714 free(m);
715 }
716
717 k += q;
718 continue;
719 }
720
721 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
722 ret[k] = replace_env(*i, env, 0);
723 if (!ret[k]) {
724 strv_free(ret);
725 return NULL((void*)0);
726 }
727 k++;
728 }
729
730 ret[k] = NULL((void*)0);
731 return ret;
732}
733
734int getenv_bool(const char *p) {
735 const char *e;
736
737 e = getenv(p);
738 if (!e)
739 return -ENXIO6;
740
741 return parse_boolean(e);
742}
743
744int getenv_bool_secure(const char *p) {
745 const char *e;
746
747 e = secure_getenv(p);
748 if (!e)
749 return -ENXIO6;
750
751 return parse_boolean(e);
752}
753
754int serialize_environment(FILE *f, char **environment) {
755 char **e;
756
757 STRV_FOREACH(e, environment)for ((e) = (environment); (e) && *(e); (e)++) {
758 _cleanup_free___attribute__((cleanup(freep))) char *ce;
759
760 ce = cescape(*e);
761 if (!ce)
762 return -ENOMEM12;
763
764 fprintf(f, "env=%s\n", ce);
765 }
766
767 /* caller should call ferror() */
768
769 return 0;
770}
771
772int deserialize_environment(char ***environment, const char *line) {
773 char *uce;
774 int r;
775
776 assert(line)do { if ((__builtin_expect(!!(!(line)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("line"), "../src/basic/env-util.c", 776,
__PRETTY_FUNCTION__); } while (0)
;
777 assert(environment)do { if ((__builtin_expect(!!(!(environment)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("environment"), "../src/basic/env-util.c"
, 777, __PRETTY_FUNCTION__); } while (0)
;
778
779 assert(startswith(line, "env="))do { if ((__builtin_expect(!!(!(startswith(line, "env="))),0)
)) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("startswith(line, \"env=\")"
), "../src/basic/env-util.c", 779, __PRETTY_FUNCTION__); } while
(0)
;
780 r = cunescape(line + 4, 0, &uce);
781 if (r < 0)
782 return r;
783
784 return strv_env_replace(environment, uce);
785}

../src/basic/alloc-util.h

1/* SPDX-License-Identifier: LGPL-2.1+ */
2#pragma once
3
4#include <alloca.h>
5#include <stddef.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include "macro.h"
10
11#define new(t, n)((t*) malloc_multiply(sizeof(t), (n))) ((t*) malloc_multiply(sizeof(t), (n)))
12
13#define new0(t, n)((t*) calloc((n), sizeof(t))) ((t*) calloc((n), sizeof(t)))
14
15#define newa(t, n)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 15, __PRETTY_FUNCTION__); } while
(0); (t*) __builtin_alloca (sizeof(t)*(n)); })
\
16 ({ \
17 assert(!size_multiply_overflow(sizeof(t), n))do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 17, __PRETTY_FUNCTION__); } while
(0)
; \
18 (t*) alloca(sizeof(t)*(n))__builtin_alloca (sizeof(t)*(n)); \
19 })
20
21#define newa0(t, n)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 21, __PRETTY_FUNCTION__); } while
(0); (t*) ({ char *_new_; size_t _len_ = sizeof(t)*(n); _new_
= __builtin_alloca (_len_); (void *) memset(_new_, 0, _len_)
; }); })
\
22 ({ \
23 assert(!size_multiply_overflow(sizeof(t), n))do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(t), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!size_multiply_overflow(sizeof(t), n)"
), "../src/basic/alloc-util.h", 23, __PRETTY_FUNCTION__); } while
(0)
; \
24 (t*) alloca0(sizeof(t)*(n))({ char *_new_; size_t _len_ = sizeof(t)*(n); _new_ = __builtin_alloca
(_len_); (void *) memset(_new_, 0, _len_); })
; \
25 })
26
27#define newdup(t, p, n)((t*) memdup_multiply(p, sizeof(t), (n))) ((t*) memdup_multiply(p, sizeof(t), (n)))
28
29#define newdup_suffix0(t, p, n)((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
30
31#define malloc0(n)(calloc(1, (n))) (calloc(1, (n)))
32
33static inline void *mfree(void *memory) {
34 free(memory);
35 return NULL((void*)0);
36}
37
38#define free_and_replace(a, b)({ free(a); (a) = (b); (b) = ((void*)0); 0; }) \
39 ({ \
40 free(a); \
41 (a) = (b); \
42 (b) = NULL((void*)0); \
43 0; \
44 })
45
46void* memdup(const void *p, size_t l) _alloc_(2);
47void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
48
49static inline void freep(void *p) {
50 free(*(void**) p);
51}
52
53#define _cleanup_free___attribute__((cleanup(freep))) _cleanup_(freep)__attribute__((cleanup(freep)))
54
55static inline bool_Bool size_multiply_overflow(size_t size, size_t need) {
56 return _unlikely_(need != 0 && size > (SIZE_MAX / need))(__builtin_expect(!!(need != 0 && size > ((18446744073709551615UL
) / need)),0))
;
57}
58
59_malloc___attribute__ ((malloc)) _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
60 if (size_multiply_overflow(size, need))
6
Taking false branch
61 return NULL((void*)0);
62
63 return malloc(size * need);
7
Memory is allocated
64}
65
66#if !HAVE_REALLOCARRAY1
67_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) {
68 if (size_multiply_overflow(size, need))
69 return NULL((void*)0);
70
71 return realloc(p, size * need);
72}
73#endif
74
75_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
76 if (size_multiply_overflow(size, need))
77 return NULL((void*)0);
78
79 return memdup(p, size * need);
80}
81
82_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
83 if (size_multiply_overflow(size, need))
84 return NULL((void*)0);
85
86 return memdup_suffix0(p, size * need);
87}
88
89void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
90void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
91
92#define GREEDY_REALLOC(array, allocated, need)greedy_realloc((void**) &(array), &(allocated), (need
), sizeof((array)[0]))
\
93 greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
94
95#define GREEDY_REALLOC0(array, allocated, need)greedy_realloc0((void**) &(array), &(allocated), (need
), sizeof((array)[0]))
\
96 greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
97
98#define alloca0(n)({ char *_new_; size_t _len_ = n; _new_ = __builtin_alloca (_len_
); (void *) memset(_new_, 0, _len_); })
\
99 ({ \
100 char *_new_; \
101 size_t _len_ = n; \
102 _new_ = alloca(_len_)__builtin_alloca (_len_); \
103 (void *) memset(_new_, 0, _len_); \
104 })
105
106/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
107#define alloca_align(size, align)({ void *_ptr_; size_t _mask_ = (align) - 1; _ptr_ = __builtin_alloca
((size) + _mask_); (void*)(((uintptr_t)_ptr_ + _mask_) &
~_mask_); })
\
108 ({ \
109 void *_ptr_; \
110 size_t _mask_ = (align) - 1; \
111 _ptr_ = alloca((size) + _mask_)__builtin_alloca ((size) + _mask_); \
112 (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
113 })
114
115#define alloca0_align(size, align)({ void *_new_; size_t _size_ = (size); _new_ = ({ void *_ptr_
; size_t _mask_ = ((align)) - 1; _ptr_ = __builtin_alloca ((_size_
) + _mask_); (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_
); }); (void*)memset(_new_, 0, _size_); })
\
116 ({ \
117 void *_new_; \
118 size_t _size_ = (size); \
119 _new_ = alloca_align(_size_, (align))({ void *_ptr_; size_t _mask_ = ((align)) - 1; _ptr_ = __builtin_alloca
((_size_) + _mask_); (void*)(((uintptr_t)_ptr_ + _mask_) &
~_mask_); })
; \
120 (void*)memset(_new_, 0, _size_); \
121 })
122
123/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to
124 * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
125#define TAKE_PTR(ptr)({ typeof(ptr) _ptr_ = (ptr); (ptr) = ((void*)0); _ptr_; }) \
126 ({ \
127 typeof(ptr) _ptr_ = (ptr); \
128 (ptr) = NULL((void*)0); \
129 _ptr_; \
130 })