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