Home | History | Annotate | Download | only in minijail
      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 #include "util.h"
      7 
      8 #include <ctype.h>
      9 #include <errno.h>
     10 #include <limits.h>
     11 #include <stdarg.h>
     12 #include <stdbool.h>
     13 #include <stdint.h>
     14 #include <stdio.h>
     15 #include <string.h>
     16 
     17 #include "libconstants.h"
     18 #include "libsyscalls.h"
     19 
     20 /*
     21  * These are syscalls used by the syslog() C library call.  You can find them
     22  * by running a simple test program.  See below for x86_64 behavior:
     23  * $ cat test.c
     24  * #include <syslog.h>
     25  * main() { syslog(0, "foo"); }
     26  * $ gcc test.c -static
     27  * $ strace ./a.out
     28  * ...
     29  * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection
     30  * connect(...)                                    <- important
     31  * sendto(...)                                     <- important
     32  * exit_group(0)                                   <- finish!
     33  */
     34 #if defined(__x86_64__)
     35 #if defined(__ANDROID__)
     36 const char *log_syscalls[] = {"socket", "connect", "fcntl", "writev"};
     37 #else
     38 const char *log_syscalls[] = {"socket", "connect", "sendto", "writev"};
     39 #endif
     40 #elif defined(__i386__)
     41 #if defined(__ANDROID__)
     42 const char *log_syscalls[] = {"socketcall", "writev", "fcntl64",
     43 			      "clock_gettime"};
     44 #else
     45 const char *log_syscalls[] = {"socketcall", "time", "writev"};
     46 #endif
     47 #elif defined(__arm__)
     48 #if defined(__ANDROID__)
     49 const char *log_syscalls[] = {"clock_gettime", "connect", "fcntl64", "socket",
     50 			      "writev"};
     51 #else
     52 const char *log_syscalls[] = {"socket", "connect", "gettimeofday", "send",
     53 			      "writev"};
     54 #endif
     55 #elif defined(__aarch64__)
     56 #if defined(__ANDROID__)
     57 const char *log_syscalls[] = {"connect", "fcntl", "sendto", "socket", "writev"};
     58 #else
     59 const char *log_syscalls[] = {"socket", "connect", "send", "writev"};
     60 #endif
     61 #elif defined(__powerpc__) || defined(__ia64__) || defined(__hppa__) ||        \
     62       defined(__sparc__) || defined(__mips__)
     63 const char *log_syscalls[] = {"socket", "connect", "send"};
     64 #else
     65 #error "Unsupported platform"
     66 #endif
     67 
     68 const size_t log_syscalls_len = ARRAY_SIZE(log_syscalls);
     69 
     70 /* clang-format off */
     71 static struct logging_config_t {
     72 	/* The logging system to use. The default is syslog. */
     73 	enum logging_system_t logger;
     74 
     75 	/* File descriptor to log to. Only used when logger is LOG_TO_FD. */
     76 	int fd;
     77 
     78 	/* Minimum priority to log. Only used when logger is LOG_TO_FD. */
     79 	int min_priority;
     80 } logging_config = {
     81 	.logger = LOG_TO_SYSLOG,
     82 };
     83 /* clang-format on */
     84 
     85 #if defined(USE_EXIT_ON_DIE)
     86 #define do_abort() exit(1)
     87 #else
     88 #define do_abort() abort()
     89 #endif
     90 
     91 #if defined(__clang__)
     92 #define attribute_no_optimize __attribute__((optnone))
     93 #else
     94 #define attribute_no_optimize __attribute__((__optimize__(0)))
     95 #endif
     96 
     97 /* Forces the compiler to perform no optimizations on |var|. */
     98 static void attribute_no_optimize alias(const void *var)
     99 {
    100 	(void)var;
    101 }
    102 
    103 void do_fatal_log(int priority, const char *format, ...)
    104 {
    105 	va_list args, stack_args;
    106 	va_start(args, format);
    107 	va_copy(stack_args, args);
    108 	if (logging_config.logger == LOG_TO_SYSLOG) {
    109 		vsyslog(priority, format, args);
    110 	} else {
    111 		vdprintf(logging_config.fd, format, args);
    112 		dprintf(logging_config.fd, "\n");
    113 	}
    114 	va_end(args);
    115 
    116 	/*
    117 	 * Write another copy of the first few characters of the message into a
    118 	 * stack-based buffer so that it can appear in minidumps. Choosing a
    119 	 * small-ish buffer size since breakpad will only pick up the first few
    120 	 * kilobytes of each stack, so that will prevent this buffer from
    121 	 * kicking out other stack frames.
    122 	 */
    123 	char log_line[512];
    124 	vsnprintf(log_line, sizeof(log_line), format, stack_args);
    125 	va_end(stack_args);
    126 	alias(log_line);
    127 	do_abort();
    128 }
    129 
    130 void do_log(int priority, const char *format, ...)
    131 {
    132 	if (logging_config.logger == LOG_TO_SYSLOG) {
    133 		va_list args;
    134 		va_start(args, format);
    135 		vsyslog(priority, format, args);
    136 		va_end(args);
    137 		return;
    138 	}
    139 
    140 	if (logging_config.min_priority < priority)
    141 		return;
    142 
    143 	va_list args;
    144 	va_start(args, format);
    145 	vdprintf(logging_config.fd, format, args);
    146 	va_end(args);
    147 	dprintf(logging_config.fd, "\n");
    148 }
    149 
    150 int lookup_syscall(const char *name)
    151 {
    152 	const struct syscall_entry *entry = syscall_table;
    153 	for (; entry->name && entry->nr >= 0; ++entry)
    154 		if (!strcmp(entry->name, name))
    155 			return entry->nr;
    156 	return -1;
    157 }
    158 
    159 const char *lookup_syscall_name(int nr)
    160 {
    161 	const struct syscall_entry *entry = syscall_table;
    162 	for (; entry->name && entry->nr >= 0; ++entry)
    163 		if (entry->nr == nr)
    164 			return entry->name;
    165 	return NULL;
    166 }
    167 
    168 long int parse_single_constant(char *constant_str, char **endptr)
    169 {
    170 	const struct constant_entry *entry = constant_table;
    171 	long int res = 0;
    172 	for (; entry->name; ++entry) {
    173 		if (!strcmp(entry->name, constant_str)) {
    174 			*endptr = constant_str + strlen(constant_str);
    175 			return entry->value;
    176 		}
    177 	}
    178 
    179 	errno = 0;
    180 	res = strtol(constant_str, endptr, 0);
    181 	if (errno == ERANGE) {
    182 		if (res == LONG_MAX) {
    183 			/* See if the constant fits in an unsigned long int. */
    184 			errno = 0;
    185 			res = strtoul(constant_str, endptr, 0);
    186 			if (errno == ERANGE) {
    187 				/*
    188 				 * On unsigned overflow, use the same convention
    189 				 * as when strtol(3) finds no digits: set
    190 				 * |*endptr| to |constant_str| and return 0.
    191 				 */
    192 				warn("unsigned overflow: '%s'", constant_str);
    193 				*endptr = constant_str;
    194 				return 0;
    195 			}
    196 		} else if (res == LONG_MIN) {
    197 			/*
    198 			 * Same for signed underflow: set |*endptr| to
    199 			 * |constant_str| and return 0.
    200 			 */
    201 			warn("signed underflow: '%s'", constant_str);
    202 			*endptr = constant_str;
    203 			return 0;
    204 		}
    205 	}
    206 	if (**endptr != '\0') {
    207 		warn("trailing garbage after constant: '%s'", constant_str);
    208 		*endptr = constant_str;
    209 		return 0;
    210 	}
    211 	return res;
    212 }
    213 
    214 static char *tokenize_parenthesized_expression(char **stringp)
    215 {
    216 	char *ret = NULL, *found = NULL;
    217 	size_t paren_count = 1;
    218 
    219 	/* If the string is NULL, there are no parens to be found. */
    220 	if (stringp == NULL || *stringp == NULL)
    221 		return NULL;
    222 
    223 	/* If the string is not on an open paren, the results are undefined. */
    224 	if (**stringp != '(')
    225 		return NULL;
    226 
    227 	for (found = *stringp + 1; *found; ++found) {
    228 		switch (*found) {
    229 		case '(':
    230 			++paren_count;
    231 			break;
    232 		case ')':
    233 			--paren_count;
    234 			if (!paren_count) {
    235 				*found = '\0';
    236 				ret = *stringp + 1;
    237 				*stringp = found + 1;
    238 				return ret;
    239 			}
    240 			break;
    241 		}
    242 	}
    243 
    244 	/* We got to the end without finding the closing paren. */
    245 	warn("unclosed parenthesis: '%s'", *stringp);
    246 	return NULL;
    247 }
    248 
    249 long int parse_constant(char *constant_str, char **endptr)
    250 {
    251 	long int value = 0, current_value;
    252 	char *group, *lastpos = constant_str;
    253 
    254 	/*
    255 	 * If |endptr| is provided, parsing errors are signaled as |endptr|
    256 	 * pointing to |constant_str|.
    257 	 */
    258 	if (endptr)
    259 		*endptr = constant_str;
    260 
    261 	/*
    262 	 * Try to parse constant expressions. Valid constant expressions are:
    263 	 *
    264 	 * - A number that can be parsed with strtol(3).
    265 	 * - A named constant expression.
    266 	 * - A parenthesized, valid constant expression.
    267 	 * - A valid constant expression prefixed with the unary bitwise
    268 	 *   complement operator ~.
    269 	 * - A series of valid constant expressions separated by pipes.  Note
    270 	 *   that since |constant_str| is an atom, there can be no spaces
    271 	 *   between the constant and the pipe.
    272 	 *
    273 	 * If there is an error parsing any of the constants, the whole process
    274 	 * fails.
    275 	 */
    276 	while (constant_str && *constant_str) {
    277 		bool negate = false;
    278 		if (*constant_str == '~') {
    279 			negate = true;
    280 			++constant_str;
    281 		}
    282 		if (*constant_str == '(') {
    283 			group =
    284 			    tokenize_parenthesized_expression(&constant_str);
    285 			if (group == NULL)
    286 				return 0;
    287 			char *end = group;
    288 			/* Recursively parse the parenthesized subexpression. */
    289 			current_value = parse_constant(group, &end);
    290 			if (end == group)
    291 				return 0;
    292 			if (constant_str && *constant_str) {
    293 				/*
    294 				 * If this is not the end of the atom, there
    295 				 * should be another | followed by more stuff.
    296 				 */
    297 				if (*constant_str != '|') {
    298 					warn("unterminated constant "
    299 					     "expression: '%s'",
    300 					     constant_str);
    301 					return 0;
    302 				}
    303 				++constant_str;
    304 				if (*constant_str == '\0') {
    305 					warn("unterminated constant "
    306 					     "expression: '%s'",
    307 					     constant_str);
    308 					return 0;
    309 				}
    310 			}
    311 			lastpos = end;
    312 		} else {
    313 			group = tokenize(&constant_str, "|");
    314 			char *end = group;
    315 			current_value = parse_single_constant(group, &end);
    316 			if (end == group)
    317 				return 0;
    318 			lastpos = end;
    319 		}
    320 		if (negate)
    321 			current_value = ~current_value;
    322 		value |= current_value;
    323 	}
    324 	if (endptr)
    325 		*endptr = lastpos;
    326 	return value;
    327 }
    328 
    329 /*
    330  * parse_size, specified as a string with a decimal number in bytes,
    331  * possibly with one 1-character suffix like "10K" or "6G".
    332  * Assumes both pointers are non-NULL.
    333  *
    334  * Returns 0 on success, negative errno on failure.
    335  * Only writes to result on success.
    336  */
    337 int parse_size(size_t *result, const char *sizespec)
    338 {
    339 	const char prefixes[] = "KMGTPE";
    340 	size_t i, multiplier = 1, nsize, size = 0;
    341 	unsigned long long parsed;
    342 	const size_t len = strlen(sizespec);
    343 	char *end;
    344 
    345 	if (len == 0 || sizespec[0] == '-')
    346 		return -EINVAL;
    347 
    348 	for (i = 0; i < sizeof(prefixes); ++i) {
    349 		if (sizespec[len - 1] == prefixes[i]) {
    350 #if __WORDSIZE == 32
    351 			if (i >= 3)
    352 				return -ERANGE;
    353 #endif
    354 			multiplier = 1024;
    355 			while (i-- > 0)
    356 				multiplier *= 1024;
    357 			break;
    358 		}
    359 	}
    360 
    361 	/* We only need size_t but strtoul(3) is too small on IL32P64. */
    362 	parsed = strtoull(sizespec, &end, 10);
    363 	if (parsed == ULLONG_MAX)
    364 		return -errno;
    365 	if (parsed >= SIZE_MAX)
    366 		return -ERANGE;
    367 	if ((multiplier != 1 && end != sizespec + len - 1) ||
    368 	    (multiplier == 1 && end != sizespec + len))
    369 		return -EINVAL;
    370 	size = (size_t)parsed;
    371 
    372 	nsize = size * multiplier;
    373 	if (nsize / multiplier != size)
    374 		return -ERANGE;
    375 	*result = nsize;
    376 	return 0;
    377 }
    378 
    379 char *strip(char *s)
    380 {
    381 	char *end;
    382 	while (*s && isblank(*s))
    383 		s++;
    384 	end = s + strlen(s) - 1;
    385 	while (end >= s && *end && (isblank(*end) || *end == '\n'))
    386 		end--;
    387 	*(end + 1) = '\0';
    388 	return s;
    389 }
    390 
    391 char *tokenize(char **stringp, const char *delim)
    392 {
    393 	char *ret = NULL;
    394 
    395 	/* If the string is NULL, there are no tokens to be found. */
    396 	if (stringp == NULL || *stringp == NULL)
    397 		return NULL;
    398 
    399 	/*
    400 	 * If the delimiter is NULL or empty,
    401 	 * the full string makes up the only token.
    402 	 */
    403 	if (delim == NULL || *delim == '\0') {
    404 		ret = *stringp;
    405 		*stringp = NULL;
    406 		return ret;
    407 	}
    408 
    409 	char *found = strstr(*stringp, delim);
    410 	if (!found) {
    411 		/*
    412 		 * The delimiter was not found, so the full string
    413 		 * makes up the only token, and we're done.
    414 		 */
    415 		ret = *stringp;
    416 		*stringp = NULL;
    417 	} else {
    418 		/* There's a token here, possibly empty.  That's OK. */
    419 		*found = '\0';
    420 		ret = *stringp;
    421 		*stringp = found + strlen(delim);
    422 	}
    423 
    424 	return ret;
    425 }
    426 
    427 char *path_join(const char *external_path, const char *internal_path)
    428 {
    429 	char *path;
    430 	size_t pathlen;
    431 
    432 	/* One extra char for '/' and one for '\0', hence + 2. */
    433 	pathlen = strlen(external_path) + strlen(internal_path) + 2;
    434 	path = malloc(pathlen);
    435 	snprintf(path, pathlen, "%s/%s", external_path, internal_path);
    436 
    437 	return path;
    438 }
    439 
    440 void *consumebytes(size_t length, char **buf, size_t *buflength)
    441 {
    442 	char *p = *buf;
    443 	if (length > *buflength)
    444 		return NULL;
    445 	*buf += length;
    446 	*buflength -= length;
    447 	return p;
    448 }
    449 
    450 char *consumestr(char **buf, size_t *buflength)
    451 {
    452 	size_t len = strnlen(*buf, *buflength);
    453 	if (len == *buflength)
    454 		/* There's no null-terminator. */
    455 		return NULL;
    456 	return consumebytes(len + 1, buf, buflength);
    457 }
    458 
    459 void init_logging(enum logging_system_t logger, int fd, int min_priority)
    460 {
    461 	logging_config.logger = logger;
    462 	logging_config.fd = fd;
    463 	logging_config.min_priority = min_priority;
    464 }
    465