• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2012 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #define _GNU_SOURCE
7 
8 #include "util.h"
9 
10 #include <ctype.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <string.h>
18 
19 #include "libconstants.h"
20 #include "libsyscalls.h"
21 
22 /*
23  * These are syscalls used by the syslog() C library call.  You can find them
24  * by running a simple test program.  See below for x86_64 behavior:
25  * $ cat test.c
26  * #include <syslog.h>
27  * main() { syslog(0, "foo"); }
28  * $ gcc test.c -static
29  * $ strace ./a.out
30  * ...
31  * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection
32  * connect(...)                                    <- important
33  * sendto(...)                                     <- important
34  * exit_group(0)                                   <- finish!
35  */
36 const char *const log_syscalls[] = {
37 #if defined(__x86_64__)
38 #if defined(__ANDROID__)
39     "socket",
40     "connect",
41     "fcntl",
42     "writev",
43 #else
44     "socket",
45     "connect",
46     "sendto",
47     "writev",
48 #endif
49 #elif defined(__i386__)
50 #if defined(__ANDROID__)
51     "socketcall",
52     "writev",
53     "fcntl64",
54     "clock_gettime",
55 #else
56     "socketcall",
57     "time",
58     "writev",
59 #endif
60 #elif defined(__arm__)
61 #if defined(__ANDROID__)
62     "clock_gettime", "connect", "fcntl64", "socket", "writev",
63 #else
64     "socket", "connect", "gettimeofday", "send", "writev",
65 #endif
66 #elif defined(__aarch64__)
67 #if defined(__ANDROID__)
68     "connect", "fcntl", "sendto", "socket", "writev",
69 #else
70     "socket",
71     "connect",
72     "send",
73     "writev",
74 #endif
75 #elif defined(__hppa__) || defined(__ia64__) || defined(__mips__) ||           \
76     defined(__powerpc__) || defined(__sparc__)
77     "socket",
78     "connect",
79     "send",
80 #elif defined(__riscv)
81 #if defined(__ANDROID__)
82     "connect", "fcntl", "sendto", "socket", "writev",
83 #else
84     "socket",
85     "connect",
86     "sendto",
87 #endif
88 #else
89 #error "Unsupported platform"
90 #endif
91 };
92 
93 const size_t log_syscalls_len = ARRAY_SIZE(log_syscalls);
94 
95 /*
96  * These syscalls are globally allowed. ChromeOS devs: Do **not** add to this
97  * list without approval from the security team.
98  *
99  * This list should be made empty (and mostly remain so) after a better
100  * mechanism is implemented: b/393353891
101  */
102 const char *const libc_compatibility_syscalls[] = {
103     "fstat",
104 #if defined(__arm__)
105     "fstat64",
106 #endif
107 };
108 
109 const size_t libc_compatibility_syscalls_len =
110     ARRAY_SIZE(libc_compatibility_syscalls);
111 
112 /* clang-format off */
113 static struct logging_config_t {
114 	/* The logging system to use. The default is syslog. */
115 	enum logging_system_t logger;
116 
117 	/* File descriptor to log to. Only used when logger is LOG_TO_FD. */
118 	int fd;
119 
120 	/* Minimum priority to log. Only used when logger is LOG_TO_FD. */
121 	int min_priority;
122 } logging_config = {
123 	.logger = LOG_TO_SYSLOG,
124 };
125 /* clang-format on */
126 
127 #if defined(USE_EXIT_ON_DIE)
128 #define do_abort() exit(1)
129 #else
130 #define do_abort() abort()
131 #endif
132 
133 #if defined(__clang__)
134 #define attribute_no_optimize __attribute__((optnone))
135 #else
136 #define attribute_no_optimize __attribute__((__optimize__(0)))
137 #endif
138 
139 /* Forces the compiler to perform no optimizations on |var|. */
alias(const void * var)140 static void attribute_no_optimize alias(const void *var)
141 {
142 	(void)var;
143 }
144 
do_fatal_log(int priority,const char * format,...)145 void do_fatal_log(int priority, const char *format, ...)
146 {
147 	va_list args, stack_args;
148 	va_start(args, format);
149 	va_copy(stack_args, args);
150 	if (logging_config.logger == LOG_TO_SYSLOG) {
151 		vsyslog(priority, format, args);
152 	} else {
153 		vdprintf(logging_config.fd, format, args);
154 		dprintf(logging_config.fd, "\n");
155 	}
156 	va_end(args);
157 
158 	/*
159 	 * Write another copy of the first few characters of the message into a
160 	 * stack-based buffer so that it can appear in minidumps. Choosing a
161 	 * small-ish buffer size since breakpad will only pick up the first few
162 	 * kilobytes of each stack, so that will prevent this buffer from
163 	 * kicking out other stack frames.
164 	 */
165 	char log_line[512];
166 	vsnprintf(log_line, sizeof(log_line), format, stack_args);
167 	va_end(stack_args);
168 	alias(log_line);
169 	do_abort();
170 }
171 
do_log(int priority,const char * format,...)172 void do_log(int priority, const char *format, ...)
173 {
174 	if (logging_config.logger == LOG_TO_SYSLOG) {
175 		va_list args;
176 		va_start(args, format);
177 		vsyslog(priority, format, args);
178 		va_end(args);
179 		return;
180 	}
181 
182 	if (logging_config.min_priority < priority)
183 		return;
184 
185 	va_list args;
186 	va_start(args, format);
187 	vdprintf(logging_config.fd, format, args);
188 	va_end(args);
189 	dprintf(logging_config.fd, "\n");
190 }
191 
192 /*
193  * Returns the syscall nr and optionally populates the index in the pointer
194  * |ind| if it is non-NULL.
195  */
lookup_syscall(const char * name,size_t * ind)196 int lookup_syscall(const char *name, size_t *ind)
197 {
198 	size_t ind_tmp = 0;
199 	const struct syscall_entry *entry = syscall_table;
200 	for (; entry->name && entry->nr >= 0; ++entry) {
201 		if (streq(entry->name, name)) {
202 			if (ind != NULL)
203 				*ind = ind_tmp;
204 			return entry->nr;
205 		}
206 		ind_tmp++;
207 	}
208 	if (ind != NULL)
209 		*ind = -1;
210 	return -1;
211 }
212 
lookup_syscall_name(long nr)213 const char *lookup_syscall_name(long nr)
214 {
215 	const struct syscall_entry *entry = syscall_table;
216 	for (; entry->name && entry->nr >= 0; ++entry)
217 		if (entry->nr == nr)
218 			return entry->name;
219 	return NULL;
220 }
221 
parse_single_constant(char * constant_str,char ** endptr)222 long int parse_single_constant(char *constant_str, char **endptr)
223 {
224 	const struct constant_entry *entry = constant_table;
225 	long int res = 0;
226 	for (; entry->name; ++entry) {
227 		if (streq(entry->name, constant_str)) {
228 			*endptr = constant_str + strlen(constant_str);
229 			return entry->value;
230 		}
231 	}
232 
233 	errno = 0;
234 	res = strtol(constant_str, endptr, 0);
235 	if (errno == ERANGE) {
236 		if (res == LONG_MAX) {
237 			/* See if the constant fits in an unsigned long int. */
238 			errno = 0;
239 			res = strtoul(constant_str, endptr, 0);
240 			if (errno == ERANGE) {
241 				/*
242 				 * On unsigned overflow, use the same convention
243 				 * as when strtol(3) finds no digits: set
244 				 * |*endptr| to |constant_str| and return 0.
245 				 */
246 				warn("unsigned overflow: '%s'", constant_str);
247 				*endptr = constant_str;
248 				return 0;
249 			}
250 		} else if (res == LONG_MIN) {
251 			/*
252 			 * Same for signed underflow: set |*endptr| to
253 			 * |constant_str| and return 0.
254 			 */
255 			warn("signed underflow: '%s'", constant_str);
256 			*endptr = constant_str;
257 			return 0;
258 		}
259 	}
260 	if (**endptr != '\0') {
261 		warn("trailing garbage after constant: '%s'", constant_str);
262 		*endptr = constant_str;
263 		return 0;
264 	}
265 	return res;
266 }
267 
tokenize_parenthesized_expression(char ** stringp)268 static char *tokenize_parenthesized_expression(char **stringp)
269 {
270 	char *ret = NULL, *found = NULL;
271 	size_t paren_count = 1;
272 
273 	/* If the string is NULL, there are no parens to be found. */
274 	if (stringp == NULL || *stringp == NULL)
275 		return NULL;
276 
277 	/* If the string is not on an open paren, the results are undefined. */
278 	if (**stringp != '(')
279 		return NULL;
280 
281 	for (found = *stringp + 1; *found; ++found) {
282 		switch (*found) {
283 		case '(':
284 			++paren_count;
285 			break;
286 		case ')':
287 			--paren_count;
288 			if (!paren_count) {
289 				*found = '\0';
290 				ret = *stringp + 1;
291 				*stringp = found + 1;
292 				return ret;
293 			}
294 			break;
295 		}
296 	}
297 
298 	/* We got to the end without finding the closing paren. */
299 	warn("unclosed parenthesis: '%s'", *stringp);
300 	return NULL;
301 }
302 
parse_constant(char * constant_str_nonnull,char ** endptr)303 long int parse_constant(char *constant_str_nonnull, char **endptr)
304 {
305 	long int value = 0, current_value;
306 	/*
307 	 * The function API says both inputs have to be non-NULL.  The code
308 	 * happens to handle NULL pointers because it resuses the input pointer
309 	 * as it tokenizes/walks it until the tokenize functions sets it to
310 	 * NULL.  But because of the attributes on the function arguments, the
311 	 * compiler incorrectly assumes the variable can't become NULL in here,
312 	 * so we have to create another variable to effectively cast it away.
313 	 */
314 	char *constant_str = constant_str_nonnull;
315 	char *group, *lastpos = constant_str;
316 
317 	/*
318 	 * If |endptr| is provided, parsing errors are signaled as |endptr|
319 	 * pointing to |constant_str|.
320 	 */
321 	if (endptr)
322 		*endptr = constant_str;
323 
324 	/*
325 	 * Try to parse constant expressions. Valid constant expressions are:
326 	 *
327 	 * - A number that can be parsed with strtol(3).
328 	 * - A named constant expression.
329 	 * - A parenthesized, valid constant expression.
330 	 * - A valid constant expression prefixed with the unary bitwise
331 	 *   complement operator ~.
332 	 * - A series of valid constant expressions separated by pipes.  Note
333 	 *   that since |constant_str| is an atom, there can be no spaces
334 	 *   between the constant and the pipe.
335 	 *
336 	 * If there is an error parsing any of the constants, the whole process
337 	 * fails.
338 	 */
339 	while (constant_str && *constant_str) {
340 		bool negate = false;
341 		if (*constant_str == '~') {
342 			negate = true;
343 			++constant_str;
344 		}
345 		if (*constant_str == '(') {
346 			group =
347 			    tokenize_parenthesized_expression(&constant_str);
348 			if (group == NULL)
349 				return 0;
350 			char *end = group;
351 			/* Recursively parse the parenthesized subexpression. */
352 			current_value = parse_constant(group, &end);
353 			if (end == group)
354 				return 0;
355 			if (constant_str && *constant_str) {
356 				/*
357 				 * If this is not the end of the atom, there
358 				 * should be another | followed by more stuff.
359 				 */
360 				if (*constant_str != '|') {
361 					warn("unterminated constant "
362 					     "expression: '%s'",
363 					     constant_str);
364 					return 0;
365 				}
366 				++constant_str;
367 				if (*constant_str == '\0') {
368 					warn("unterminated constant "
369 					     "expression: '%s'",
370 					     constant_str);
371 					return 0;
372 				}
373 			}
374 			lastpos = end;
375 		} else {
376 			group = tokenize(&constant_str, "|");
377 			char *end = group;
378 			current_value = parse_single_constant(group, &end);
379 			if (end == group)
380 				return 0;
381 			lastpos = end;
382 		}
383 		if (negate)
384 			current_value = ~current_value;
385 		value |= current_value;
386 	}
387 	if (endptr)
388 		*endptr = lastpos;
389 	return value;
390 }
391 
parse_size(uint64_t * result,const char * sizespec)392 int parse_size(uint64_t *result, const char *sizespec)
393 {
394 	uint64_t size;
395 	unsigned long long parsed;
396 	char *end;
397 
398 	/* strtoull supports leading whitespace, -, and + signs. */
399 	if (sizespec[0] < '0' || sizespec[0] > '9')
400 		return -EINVAL;
401 
402 	/* Clear+check errno so we handle ULLONG_MAX correctly. */
403 	errno = 0;
404 	parsed = strtoull(sizespec, &end, 10);
405 	if (errno)
406 		return -errno;
407 	size = parsed;
408 
409 	/* See if there's a suffix. */
410 	if (*end != '\0') {
411 		static const char suffixes[] = "KMGTPE";
412 		size_t i;
413 
414 		/* Only allow 1 suffix. */
415 		if (end[1] != '\0')
416 			return -EINVAL;
417 
418 		for (i = 0; i < sizeof(suffixes) - 1; ++i) {
419 			if (*end == suffixes[i]) {
420 				/* Make sure we don't overflow. */
421 				const int scale = (i + 1) * 10;
422 				uint64_t mask =
423 				    ~((UINT64_C(1) << (64 - scale)) - 1);
424 				if (size & mask)
425 					return -ERANGE;
426 				size <<= scale;
427 				break;
428 			}
429 		}
430 
431 		/* Unknown suffix. */
432 		if (i == sizeof(suffixes) - 1)
433 			return -EINVAL;
434 	}
435 
436 	*result = size;
437 	return 0;
438 }
439 
strip(char * s)440 char *strip(char *s)
441 {
442 	char *end;
443 	while (*s && isblank(*s))
444 		s++;
445 	end = s + strlen(s) - 1;
446 	while (end >= s && *end && (isblank(*end) || *end == '\n'))
447 		end--;
448 	*(end + 1) = '\0';
449 	return s;
450 }
451 
tokenize(char ** stringp,const char * delim)452 char *tokenize(char **stringp, const char *delim)
453 {
454 	char *ret = NULL;
455 
456 	/* If the string is NULL, there are no tokens to be found. */
457 	if (stringp == NULL || *stringp == NULL)
458 		return NULL;
459 
460 	/*
461 	 * If the delimiter is NULL or empty,
462 	 * the full string makes up the only token.
463 	 */
464 	if (delim == NULL || *delim == '\0') {
465 		ret = *stringp;
466 		*stringp = NULL;
467 		return ret;
468 	}
469 
470 	char *found = strstr(*stringp, delim);
471 	if (!found) {
472 		/*
473 		 * The delimiter was not found, so the full string
474 		 * makes up the only token, and we're done.
475 		 */
476 		ret = *stringp;
477 		*stringp = NULL;
478 	} else {
479 		/* There's a token here, possibly empty.  That's OK. */
480 		*found = '\0';
481 		ret = *stringp;
482 		*stringp = found + strlen(delim);
483 	}
484 
485 	return ret;
486 }
487 
path_join(const char * external_path,const char * internal_path)488 char *path_join(const char *external_path, const char *internal_path)
489 {
490 	char *path = NULL;
491 	return asprintf(&path, "%s/%s", external_path, internal_path) < 0
492 		   ? NULL
493 		   : path;
494 }
495 
path_is_parent(const char * parent,const char * child)496 bool path_is_parent(const char *parent, const char *child)
497 {
498 	/*
499 	 * -Make sure |child| starts with |parent|.
500 	 * -Make sure that if |child| is longer than |parent|, either:
501 	 * --the last character in |parent| is a path separator, or
502 	 * --the character immediately following |parent| in |child| is a path
503 	 *  separator.
504 	 */
505 	size_t parent_len = strlen(parent);
506 	return strncmp(parent, child, parent_len) == 0 &&
507 	       (strlen(child) > parent_len ? (parent[parent_len - 1] == '/' ||
508 					      child[parent_len] == '/')
509 					   : false);
510 }
511 
consumebytes(size_t length,char ** buf,size_t * buflength)512 void *consumebytes(size_t length, char **buf, size_t *buflength)
513 {
514 	char *p = *buf;
515 	if (length > *buflength)
516 		return NULL;
517 	*buf += length;
518 	*buflength -= length;
519 	return p;
520 }
521 
consumestr(char ** buf,size_t * buflength)522 char *consumestr(char **buf, size_t *buflength)
523 {
524 	size_t len = strnlen(*buf, *buflength);
525 	if (len == *buflength)
526 		/* There's no null-terminator. */
527 		return NULL;
528 	return consumebytes(len + 1, buf, buflength);
529 }
530 
init_logging(enum logging_system_t logger,int fd,int min_priority)531 void init_logging(enum logging_system_t logger, int fd, int min_priority)
532 {
533 	logging_config.logger = logger;
534 	logging_config.fd = fd;
535 	logging_config.min_priority = min_priority;
536 }
537 
minijail_free_env(char ** env)538 void minijail_free_env(char **env)
539 {
540 	if (!env)
541 		return;
542 
543 	for (char **entry = env; *entry; ++entry) {
544 		free(*entry);
545 	}
546 
547 	free(env);
548 }
549 
minijail_copy_env(char * const * env)550 char **minijail_copy_env(char *const *env)
551 {
552 	if (!env)
553 		return calloc(1, sizeof(char *));
554 
555 	int len = 0;
556 	while (env[len])
557 		++len;
558 
559 	char **copy = calloc(len + 1, sizeof(char *));
560 	if (!copy)
561 		return NULL;
562 
563 	for (char **entry = copy; *env; ++env, ++entry) {
564 		*entry = strdup(*env);
565 		if (!*entry) {
566 			minijail_free_env(copy);
567 			return NULL;
568 		}
569 	}
570 
571 	return copy;
572 }
573 
574 /*
575  * Utility function used by minijail_setenv, minijail_unsetenv and
576  * minijail_getenv, returns true if |name| is found, false if not.
577  * If found, |*i| is |name|'s index. If not, |*i| is the length of |envp|.
578  */
getenv_index(char ** envp,const char * name,int * i)579 static bool getenv_index(char **envp, const char *name, int *i)
580 {
581 	if (!envp || !name || !i)
582 		return false;
583 
584 	size_t name_len = strlen(name);
585 	for (*i = 0; envp[*i]; ++(*i)) {
586 		/*
587 		 * If we find a match the size of |name|, we must check
588 		 * that the next character is a '=', indicating that
589 		 * the full varname of envp[i] is exactly |name| and
590 		 * not just happening to start with |name|.
591 		 */
592 		if (!strncmp(envp[*i], name, name_len) &&
593 		    (envp[*i][name_len] == '=')) {
594 			return true;
595 		}
596 	}
597 	/* No match found, |*i| contains the number of elements in |envp|. */
598 	return false;
599 }
600 
minijail_setenv(char *** env,const char * name,const char * value,int overwrite)601 int minijail_setenv(char ***env, const char *name, const char *value,
602 		    int overwrite)
603 {
604 	if (!env || !*env || !name || !*name || !value)
605 		return EINVAL;
606 
607 	char **dest = NULL;
608 	int i;
609 
610 	/* Look in env to check if this var name already exists. */
611 	if (getenv_index(*env, name, &i)) {
612 		if (!overwrite)
613 			return 0;
614 		dest = &(*env)[i];
615 	}
616 
617 	char *new_entry = NULL;
618 	if (asprintf(&new_entry, "%s=%s", name, value) == -1)
619 		return ENOMEM;
620 
621 	if (dest) {
622 		free(*dest);
623 		*dest = new_entry;
624 		return 0;
625 	}
626 
627 	/* getenv_index has set |i| to the length of |env|. */
628 	++i;
629 	char **new_env = realloc(*env, (i + 1) * sizeof(char *));
630 	if (!new_env) {
631 		free(new_entry);
632 		return ENOMEM;
633 	}
634 
635 	new_env[i - 1] = new_entry;
636 	new_env[i] = NULL;
637 	*env = new_env;
638 	return 0;
639 }
640 
641 /*
642  * This is like getline() but supports line wrapping with \.
643  */
getmultiline(char ** lineptr,size_t * n,FILE * stream)644 ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream)
645 {
646 	ssize_t ret = getline(lineptr, n, stream);
647 	if (ret < 0)
648 		return ret;
649 
650 	char *line = *lineptr;
651 	/* Eat the newline to make processing below easier. */
652 	if (ret > 0 && line[ret - 1] == '\n')
653 		line[--ret] = '\0';
654 
655 	/* If the line doesn't end in a backslash, we're done. */
656 	if (ret <= 0 || line[ret - 1] != '\\')
657 		return ret;
658 
659 	/* This line ends in a backslash. Get the nextline. */
660 	line[--ret] = '\0';
661 	size_t next_n = 0;
662 	attribute_cleanup_str char *next_line = NULL;
663 	ssize_t next_ret = getmultiline(&next_line, &next_n, stream);
664 	if (next_ret == -1) {
665 		/* We couldn't fully read the line, so return an error. */
666 		return -1;
667 	}
668 
669 	/* Merge the lines. */
670 	*n = ret + next_ret + 2;
671 	line = realloc(line, *n);
672 	if (!line)
673 		return -1;
674 	line[ret] = ' ';
675 	memcpy(&line[ret + 1], next_line, next_ret + 1);
676 	*lineptr = line;
677 	return *n - 1;
678 }
679 
minijail_getenv(char ** envp,const char * name)680 char *minijail_getenv(char **envp, const char *name)
681 {
682 	if (!envp || !name)
683 		return NULL;
684 
685 	int i;
686 	if (!getenv_index(envp, name, &i))
687 		return NULL;
688 
689 	/* Return a ptr to the value after the '='. */
690 	return envp[i] + strlen(name) + 1;
691 }
692 
minijail_unsetenv(char ** envp,const char * name)693 bool minijail_unsetenv(char **envp, const char *name)
694 {
695 	if (!envp || !name)
696 		return false;
697 
698 	int i;
699 	if (!getenv_index(envp, name, &i))
700 		return false;
701 
702 	/* We found a match, replace it by the last entry of the array. */
703 	int last;
704 	for (last = i; envp[last]; ++last)
705 		continue;
706 	--last;
707 	envp[i] = envp[last];
708 	envp[last] = NULL;
709 
710 	return true;
711 }
712