Fix BPF instruction count bug.

We were accidentally capping the total number of BPF instructions at
256 when doing label fixup.

Also add a simple binary to print a compiled policy.

Bug: 31848734
Test: Policy attached to the bug works.

Change-Id: I9df058e2f4888289db0219d65ca97851fac515d0
diff --git a/Makefile b/Makefile
index 4cbf0b6..42d9a05 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,8 @@
 all: CC_BINARY(minijail0) CC_LIBRARY(libminijail.so) \
 		CC_LIBRARY(libminijailpreload.so)
 
+parse_seccomp_policy: CXX_BINARY(parse_seccomp_policy)
+
 # TODO(jorgelo): convert to TEST().
 tests: CC_BINARY(libminijail_unittest) CC_BINARY(syscall_filter_unittest)
 
@@ -30,25 +32,30 @@
 
 CC_LIBRARY(libminijail.so): LDLIBS += -lcap
 CC_LIBRARY(libminijail.so): libminijail.o syscall_filter.o signal_handler.o \
-    bpf.o util.o syscall_wrapper.o libconstants.gen.o libsyscalls.gen.o
+		bpf.o util.o syscall_wrapper.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_handler.o bpf.o util.o syscall_wrapper.o \
-		libconstants.gen.o libsyscalls.gen.o
+		syscall_filter.o signal_handler.o bpf.o util.o \
+		syscall_wrapper.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 \
-		libconstants.gen.o libsyscalls.gen.o syscall_filter.o signal_handler.o \
-		bpf.o util.o syscall_wrapper.o
+		libconstants.gen.o libsyscalls.gen.o syscall_filter.o \
+		signal_handler.o bpf.o util.o syscall_wrapper.o
 clean: CLEAN(libminijailpreload.so)
 
 CC_BINARY(syscall_filter_unittest): syscall_filter_unittest.o syscall_filter.o \
 		bpf.o util.o libconstants.gen.o libsyscalls.gen.o
 clean: CLEAN(syscall_filter_unittest)
 
+CXX_BINARY(parse_seccomp_policy): parse_seccomp_policy.o syscall_filter.o \
+		bpf.o util.o libconstants.gen.o libsyscalls.gen.o
+clean: CLEAN(parse_policy)
+
 libsyscalls.gen.o: CPPFLAGS += -I$(SRC)
 
 libsyscalls.gen.o.depends: libsyscalls.gen.c
diff --git a/bpf.c b/bpf.c
index 20f2ed0..f56bc11 100644
--- a/bpf.c
+++ b/bpf.c
@@ -172,8 +172,8 @@
 	printf("len == %d\n", len);
 	printf("filter:\n");
 	for (i = 0; i < len; i++) {
-		printf("%d: \t{ code=%#x, jt=%u, jf=%u, k=%#x \t}\n",
-			i, filter[i].code, filter[i].jt, filter[i].jf, filter[i].k);
+		printf("%d: \t{ code=%#x, jt=%u, jf=%u, k=%#x \t}\n", i,
+		       filter[i].code, filter[i].jt, filter[i].jf, filter[i].k);
 	}
 }
 
@@ -184,41 +184,48 @@
 	dump_bpf_filter(filter, len);
 }
 
-int bpf_resolve_jumps(struct bpf_labels *labels,
-		struct sock_filter *filter, size_t count)
+int bpf_resolve_jumps(struct bpf_labels *labels, struct sock_filter *filter,
+		      size_t len)
 {
+	size_t insn;
 	struct sock_filter *begin = filter;
-	__u8 insn = count - 1;
 
-	if (count < 1)
+	/* This needs at least two instructions: one jump and one label. */
+	if (len < 2)
 		return -1;
+
+	if (len > BPF_MAXINSNS)
+		return -1;
+
+	insn = len - 1;
+
 	/*
 	 * Walk it once, backwards, to build the label table and do fixups.
 	 * Since backward jumps are disallowed by BPF, this is easy.
 	 */
 	for (filter += insn; filter >= begin; --insn, --filter) {
-		if (filter->code != (BPF_JMP+BPF_JA))
+		if (filter->code != (BPF_JMP + BPF_JA))
 			continue;
-		switch ((filter->jt<<8)|filter->jf) {
-		case (JUMP_JT<<8)|JUMP_JF:
+		switch ((filter->jt << 8) | filter->jf) {
+		case (JUMP_JT << 8) | JUMP_JF:
 			if (labels->labels[filter->k].location == 0xffffffff) {
 				fprintf(stderr, "Unresolved label: '%s'\n",
 					labels->labels[filter->k].label);
 				return 1;
 			}
-			filter->k = labels->labels[filter->k].location -
-					(insn + 1);
+			filter->k =
+			    labels->labels[filter->k].location - (insn + 1);
 			filter->jt = 0;
 			filter->jf = 0;
 			continue;
-		case (LABEL_JT<<8)|LABEL_JF:
+		case (LABEL_JT << 8) | LABEL_JF:
 			if (labels->labels[filter->k].location != 0xffffffff) {
 				fprintf(stderr, "Duplicate label use: '%s'\n",
 					labels->labels[filter->k].label);
 				return 1;
 			}
 			labels->labels[filter->k].location = insn;
-			filter->k = 0; /* fall through */
+			filter->k = 0; /* Fall through. */
 			filter->jt = 0;
 			filter->jf = 0;
 			continue;
@@ -246,6 +253,11 @@
 		if (!strcmp(label, begin->label))
 			return id;
 	}
+
+	/* The label wasn't found. Insert it only if there's space. */
+	if (labels->count == BPF_LABELS_MAX) {
+		return -1;
+	}
 	begin->label = strndup(label, MAX_BPF_LABEL_LEN);
 	if (!begin->label) {
 		return -1;
diff --git a/bpf.h b/bpf.h
index e4897e2..60f4d7b 100644
--- a/bpf.h
+++ b/bpf.h
@@ -114,7 +114,7 @@
 
 #define MAX_BPF_LABEL_LEN 32
 
-#define BPF_LABELS_MAX 256
+#define BPF_LABELS_MAX 512	/* Each syscall could have an argument block. */
 struct bpf_labels {
 	int count;
 	struct __bpf_label {
diff --git a/parse_seccomp_policy.cc b/parse_seccomp_policy.cc
new file mode 100644
index 0000000..c50b79e
--- /dev/null
+++ b/parse_seccomp_policy.cc
@@ -0,0 +1,44 @@
+// parse_seccomp_policy.cc
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+
+#include "bpf.h"
+#include "syscall_filter.h"
+#include "util.h"
+
+/* TODO(jorgelo): Use libseccomp disassembler here. */
+int main(int argc, char **argv) {
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s <policy file>\n", argv[0]);
+		return 1;
+	}
+
+	FILE *f = fopen(argv[1], "r");
+	if (!f) {
+		pdie("fopen(%s) failed", argv[1]);
+	}
+
+	struct sock_fprog fp;
+	int res = compile_filter(f, &fp, 0);
+	if (res != 0) {
+		die("compile_filter failed");
+	}
+	dump_bpf_prog(&fp);
+
+	free(fp.filter);
+	fclose(f);
+	return 0;
+}