minijail: Add named constants for seccomp filters

This makes it possible to write filters using named constants (like
ENOSYS instead of 38).

BUG=chromium:516701
TEST=syscall_filter_unittest passes.

Change-Id: Ic44cbdfb6f2228f6f658b1cc48adf5a923394306
Reviewed-on: https://chromium-review.googlesource.com/290540
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Trybot-Ready: Luis Hector Chavez <lhchavez@google.com>
Tested-by: Luis Hector Chavez <lhchavez@google.com>
Commit-Queue: Luis Hector Chavez <lhchavez@google.com>
diff --git a/Makefile b/Makefile
index 75ea871..64ba1c5 100644
--- a/Makefile
+++ b/Makefile
@@ -23,27 +23,27 @@
 tests: CC_BINARY(libminijail_unittest) CC_BINARY(syscall_filter_unittest)
 
 CC_BINARY(minijail0): LDLIBS += -lcap -ldl
-CC_BINARY(minijail0): libsyscalls.gen.o libminijail.o syscall_filter.o \
-		signal.o bpf.o util.o elfparse.o minijail0.o
+CC_BINARY(minijail0): libconstants.gen.o libsyscalls.gen.o libminijail.o \
+		syscall_filter.o signal.o bpf.o util.o elfparse.o minijail0.o
 clean: CLEAN(minijail0)
 
 CC_LIBRARY(libminijail.so): LDLIBS += -lcap
 CC_LIBRARY(libminijail.so): libminijail.o syscall_filter.o signal.o bpf.o \
-		util.o libsyscalls.gen.o
+		util.o libconstants.gen.o libsyscalls.gen.o
 clean: CLEAN(libminijail.so)
 
 CC_BINARY(libminijail_unittest): LDLIBS += -lcap
 CC_BINARY(libminijail_unittest): libminijail_unittest.o libminijail.o \
-		syscall_filter.o signal.o bpf.o util.o libsyscalls.gen.o
+		syscall_filter.o signal.o bpf.o util.o libconstants.gen.o libsyscalls.gen.o
 clean: CLEAN(libminijail_unittest)
 
 CC_LIBRARY(libminijailpreload.so): LDLIBS += -lcap -ldl
 CC_LIBRARY(libminijailpreload.so): libminijailpreload.o libminijail.o \
-		libsyscalls.gen.o syscall_filter.o signal.o bpf.o util.o
+		libconstants.gen.o libsyscalls.gen.o syscall_filter.o signal.o bpf.o util.o
 clean: CLEAN(libminijailpreload.so)
 
 CC_BINARY(syscall_filter_unittest): syscall_filter_unittest.o syscall_filter.o \
-		bpf.o util.o libsyscalls.gen.o
+		bpf.o util.o libconstants.gen.o libsyscalls.gen.o
 clean: CLEAN(syscall_filter_unittest)
 
 libsyscalls.gen.o: CPPFLAGS += -I$(SRC)
@@ -60,3 +60,18 @@
 clean: CLEAN(libsyscalls.gen.c)
 
 $(eval $(call add_object_rules,libsyscalls.gen.o,CC,c,CFLAGS))
+
+libconstants.gen.o: CPPFLAGS += -I$(SRC)
+
+libconstants.gen.o.depends: libconstants.gen.c
+
+# Only regenerate libconstants.gen.c if the Makefile or header changes.
+# NOTE! This will not detect if the file is not appropriate for the target.
+# TODO(jorgelo): fix generation when 'CC' env variable is not set.
+libconstants.gen.c: $(SRC)/Makefile $(SRC)/libconstants.h
+	@printf "Generating target-arch specific $@... "
+	$(QUIET)$(SRC)/gen_constants.sh $@
+	@printf "done.\n"
+clean: CLEAN(libconstants.gen.c)
+
+$(eval $(call add_object_rules,libconstants.gen.o,CC,c,CFLAGS))
diff --git a/gen_constants.sh b/gen_constants.sh
new file mode 100755
index 0000000..8dcbca2
--- /dev/null
+++ b/gen_constants.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Generates a header file with a named constant table made up of "name", value
+# entries by including several build target header files and emitting the list
+# of defines.  Use of the preprocessor is needed to recursively include all
+# relevant headers.
+
+set -e
+
+if [ $# -ne 1 ] && [ $# -ne 3]; then
+  echo "Usage: $(basename "$0") OUTFILE"
+  echo "Usage: $(basename "$0") CC CFLAGS OUTFILE"
+  exit 1
+fi
+
+if [ $# -eq 3 ]; then
+  CC="$1"
+  shift
+  CFLAGS="$1"
+  shift
+fi
+OUTFILE="$1"
+
+INCLUDES='
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/prctl.h>
+#include <linux/sched.h>
+#include <stddef.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>'
+
+# Passes the previous list of #includes to the C preprocessor and prints out
+# all #defines whose name is all-caps.  Excludes a few symbols that are known
+# macro functions that don't evaluate to a constant.
+cat <<-EOF > "${OUTFILE}"
+/* GENERATED BY MAKEFILE */
+$INCLUDES
+
+#include "libconstants.h"
+const struct constant_entry constant_table[] = {
+$(echo "$INCLUDES" | \
+  ${CC} ${CFLAGS} -dD - -E | \
+  grep '^#define [A-Z][A-Z0-9_]* ' | \
+  grep -v '\(SIGRTMAX\|SIGRTMIN\|SIG_\|NULL\)' | \
+  sort | \
+  uniq | \
+  sed -e 's/#define \([A-Z0-9_]\+\).*$/#ifdef \1\n  { "\1", \1 },\n#endif  \/\/ \1/')
+  { NULL, 0 },
+};
+EOF
diff --git a/libconstants.h b/libconstants.h
new file mode 100644
index 0000000..865f9d3
--- /dev/null
+++ b/libconstants.h
@@ -0,0 +1,15 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef MINIJAIL_LIBCONSTANTS_H_
+#define MINIJAIL_LIBCONSTANTS_H_
+
+struct constant_entry {
+  const char *name;
+  unsigned long value;
+};
+
+extern const struct constant_entry constant_table[];
+
+#endif  /* MINIJAIL_LIBCONSTANTS_H_ */
diff --git a/syscall_filter.c b/syscall_filter.c
index 9ea5dca..3335a07 100644
--- a/syscall_filter.c
+++ b/syscall_filter.c
@@ -182,7 +182,11 @@
 	if (argidx_ptr == argidx_str + 3)
 		return -1;
 
-	long int c = strtol(constant_str, NULL, 0);
+	char *constant_str_ptr;
+	long int c = parse_constant(constant_str, &constant_str_ptr);
+	if (constant_str_ptr == constant_str)
+		return -1;
+
 	/*
 	 * Looks up the label for the end of the AND statement
 	 * this atom belongs to.
@@ -220,10 +224,9 @@
 
 	if (errno_val_str) {
 		char *errno_val_ptr;
-		int errno_val = strtol(
-				errno_val_str, &errno_val_ptr, 0);
+		int errno_val = parse_constant(errno_val_str, &errno_val_ptr);
 		/* Checks to see if we parsed an actual errno. */
-		if (errno_val_ptr == errno_val_str)
+		if (errno_val_ptr == errno_val_str || errno_val == -1)
 			return -1;
 
 		append_ret_errno(head, errno_val);
diff --git a/syscall_filter_unittest.c b/syscall_filter_unittest.c
index fb903c5..df014c3 100644
--- a/syscall_filter_unittest.c
+++ b/syscall_filter_unittest.c
@@ -290,7 +290,7 @@
 }
 
 TEST_F(arg_filter, arg0_mask) {
-	const char *fragment = "arg1 & 02";	/* O_RDWR */
+	const char *fragment = "arg1 & O_RDWR";
 	int nr = 1;
 	unsigned int id = 0;
 	struct filter_block *block =
diff --git a/util.c b/util.c
index 550ed78..6b16a23 100644
--- a/util.c
+++ b/util.c
@@ -4,10 +4,12 @@
  */
 
 #include <ctype.h>
+#include <stdio.h>
 #include <string.h>
 
 #include "util.h"
 
+#include "libconstants.h"
 #include "libsyscalls.h"
 
 /*
@@ -56,6 +58,21 @@
 	return NULL;
 }
 
+long int parse_constant(char *constant_str, char **endptr)
+{
+	const struct constant_entry *entry = constant_table;
+	for (; entry->name; ++entry) {
+		if (!strcmp(entry->name, constant_str)) {
+			if (endptr)
+				*endptr = constant_str + strlen(constant_str);
+
+			return entry->value;
+		}
+	}
+
+	return strtol(constant_str, endptr, 0);
+}
+
 char *strip(char *s)
 {
 	char *end;
diff --git a/util.h b/util.h
index d07e5b1..a88cbc8 100644
--- a/util.h
+++ b/util.h
@@ -31,6 +31,7 @@
 
 int lookup_syscall(const char *name);
 const char *lookup_syscall_name(int nr);
+long int parse_constant(char *constant_str, char **endptr);
 char *strip(char *s);
 char *tokenize(char **stringp, const char *delim);