merge in pi-release history after reset to master
diff --git a/.gitignore b/.gitignore
index a121c9e..8713a1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
/minijail0
/syscall_filter_unittest
/system_unittest
+/util_unittest
/parse_seccomp_policy
# common.mk TEST().
diff --git a/Android.bp b/Android.bp
index 03b0530..9dd17ea 100644
--- a/Android.bp
+++ b/Android.bp
@@ -236,6 +236,34 @@
},
}
+// Utility functionality unit tests using gtest.
+//
+// For a device, run with:
+// adb shell /data/nativetest/util_unittest_gtest/util_unittest_gtest
+//
+// For host, run with:
+// out/host/linux-x86/nativetest(64)/util_unittest_gtest/util_unittest_gtest
+// =========================================================
+cc_test {
+ name: "util_unittest_gtest",
+ defaults: ["libminijail_flags"],
+ host_supported: true,
+
+ srcs: [
+ "util.c",
+ "util_unittest.cc",
+ ] + unittestSrcFiles,
+
+ static_libs: ["libminijail_generated"],
+ shared_libs: minijailCommonLibraries,
+
+ target: {
+ android: {
+ test_suites: ["device-tests"],
+ },
+ },
+}
+
// libminijail_test executable for brillo_Minijail test.
// =========================================================
cc_test {
diff --git a/Makefile b/Makefile
index b0f0509..7bbb0b4 100644
--- a/Makefile
+++ b/Makefile
@@ -55,7 +55,8 @@
tests: TEST(CXX_BINARY(libminijail_unittest)) \
TEST(CXX_BINARY(syscall_filter_unittest)) \
- TEST(CXX_BINARY(system_unittest))
+ TEST(CXX_BINARY(system_unittest)) \
+ TEST(CXX_BINARY(util_unittest)) \
CC_BINARY(minijail0): LDLIBS += -lcap -ldl
@@ -105,6 +106,16 @@
clean: CLEAN(system_unittest)
+CXX_BINARY(util_unittest): CXXFLAGS += $(GTEST_CXXFLAGS)
+CXX_BINARY(util_unittest): LDLIBS += -lcap $(GTEST_LIBS)
+ifeq ($(USE_SYSTEM_GTEST),no)
+CXX_BINARY(util_unittest): $(GTEST_LIBS)
+endif
+CXX_BINARY(util_unittest): util_unittest.o \
+ $(CORE_OBJECT_FILES) testrunner.o
+clean: CLEAN(util_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_seccomp_policy)
diff --git a/gen_constants-inl.h b/gen_constants-inl.h
index ea8c701..8efd4f6 100644
--- a/gen_constants-inl.h
+++ b/gen_constants-inl.h
@@ -10,6 +10,7 @@
#include <stddef.h>
#include <signal.h>
#include <sys/mman.h>
-#include <sys/stat.h>
#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/types.h>
diff --git a/minijail0.1 b/minijail0.1
index 9d24422..52f98e2 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -12,9 +12,10 @@
Run using the alternate syscall table named \fItable\fR. Only available on kernels
and architectures that support the \fBPR_ALT_SYSCALL\fR option of \fBprctl\fR(2).
.TP
-\fB-b <src>,<dest>[,<writeable>]
+\fB-b <src>[,<dest>[,<writeable>]]
Bind-mount \fIsrc\fR into the chroot directory at \fIdest\fR, optionally writeable.
The \fIsrc\fR path must be an absolute path.
+If \fIdest\fR is not specified, it will default to \fIsrc\fR.
If the destination does not exist, it will be created as a file or directory
based on the \fIsrc\fR type.
.TP
@@ -58,13 +59,14 @@
\fB-H\fR
Print a help message detailing supported system call names for seccomp_filter.
(Other direct numbers may be specified if minijail0 is not in sync with the
- host kernel or something like 32/64-bit compatibility issues exist.)
+host kernel or something like 32/64-bit compatibility issues exist.)
.TP
\fB-I\fR
Run \fIprogram\fR as init (pid 1) inside a new pid namespace (implies \fB-p\fR).
.TP
-\fB-k <src>,<dest>,<type>[,<flags>]\fR
-Mount \fIsrc\fR, a \fItype\fR filesystem, into the chroot directory at \fIdest\fR, with optional \fIflags\fR.
+\fB-k <src>,<dest>,<type>[,<flags>[,<data>]]\fR
+Mount \fIsrc\fR, a \fItype\fR filesystem, into the chroot directory at \fIdest\fR,
+with optional \fIflags\fR and optional \fIdata\fR (see \fBmount\fR(2)).
If the mount is not a pseudo filesystem (e.g. proc or sysfs), \fIsrc\fR path
must be an absolute path (e.g. \fI/dev/sda1\fR and not \fIsda1\fR).
If the destination does not exist, it will be created as a directory.
diff --git a/minijail0.c b/minijail0.c
index b0daed2..425fbf0 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -92,14 +92,18 @@
static void add_binding(struct minijail *j, char *arg)
{
- char *src = strtok(arg, ",");
- char *dest = strtok(NULL, ",");
- char *flags = strtok(NULL, ",");
- if (!src || !dest) {
+ char *src = tokenize(&arg, ",");
+ char *dest = tokenize(&arg, ",");
+ char *flags = tokenize(&arg, ",");
+ if (!src || src[0] == '\0' || arg != NULL) {
fprintf(stderr, "Bad binding: %s %s\n", src, dest);
exit(1);
}
- if (minijail_bind(j, src, dest, flags ? atoi(flags) : 0)) {
+ if (dest == NULL || dest[0] == '\0')
+ dest = src;
+ if (flags == NULL || flags[0] == '\0')
+ flags = "0";
+ if (minijail_bind(j, src, dest, atoi(flags))) {
fprintf(stderr, "minijail_bind failed.\n");
exit(1);
}
@@ -107,10 +111,11 @@
static void add_rlimit(struct minijail *j, char *arg)
{
- char *type = strtok(arg, ",");
- char *cur = strtok(NULL, ",");
- char *max = strtok(NULL, ",");
- if (!type || !cur || !max) {
+ char *type = tokenize(&arg, ",");
+ char *cur = tokenize(&arg, ",");
+ char *max = tokenize(&arg, ",");
+ if (!type || type[0] == '\0' || !cur || cur[0] == '\0' ||
+ !max || max[0] == '\0' || arg != NULL) {
fprintf(stderr, "Bad rlimit '%s'.\n", arg);
exit(1);
}
@@ -123,12 +128,13 @@
static void add_mount(struct minijail *j, char *arg)
{
- char *src = strtok(arg, ",");
- char *dest = strtok(NULL, ",");
- char *type = strtok(NULL, ",");
- char *flags = strtok(NULL, ",");
- char *data = strtok(NULL, ",");
- if (!src || !dest || !type) {
+ char *src = tokenize(&arg, ",");
+ char *dest = tokenize(&arg, ",");
+ char *type = tokenize(&arg, ",");
+ char *flags = tokenize(&arg, ",");
+ char *data = tokenize(&arg, ",");
+ if (!src || src[0] == '\0' || !dest || dest[0] == '\0' ||
+ !type || type[0] == '\0' || arg != NULL) {
fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
exit(1);
}
@@ -294,7 +300,7 @@
/* clang-format off */
printf("Usage: %s [-dGhHiIKlLnNprRstUvyYz]\n"
" [-a <table>]\n"
- " [-b <src>,<dest>[,<writeable>]] [-k <src>,<dest>,<type>[,<flags>][,<data>]]\n"
+ " [-b <src>[,<dest>[,<writeable>]]] [-k <src>,<dest>,<type>[,<flags>[,<data>]]]\n"
" [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
" [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*] [--profile <name>]\n"
" [-R <type,cur,max>] [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n"
diff --git a/syscall_filter_unittest.cc b/syscall_filter_unittest.cc
index 6bc044d..db01fbb 100644
--- a/syscall_filter_unittest.cc
+++ b/syscall_filter_unittest.cc
@@ -1261,6 +1261,19 @@
ASSERT_NE(res, 0);
}
+TEST(FilterTest, invalid_tokens) {
+ struct sock_fprog actual;
+ const char *policy = "read: arg0 == 1 |||| arg0 == 2\n";
+
+ FILE *policy_file = write_policy_to_pipe(policy, strlen(policy));
+ ASSERT_NE(policy_file, nullptr);
+
+ int res =
+ compile_filter("policy", policy_file, &actual, USE_RET_KILL, NO_LOGGING);
+ fclose(policy_file);
+ ASSERT_NE(res, 0);
+}
+
TEST(FilterTest, nonexistent) {
struct sock_fprog actual;
int res = compile_filter("policy", NULL, &actual, USE_RET_KILL, NO_LOGGING);
diff --git a/util.c b/util.c
index 14c028a..9bb37ca 100644
--- a/util.c
+++ b/util.c
@@ -257,8 +257,8 @@
{
char *ret = NULL;
- /* If the string is NULL or empty, there are no tokens to be found. */
- if (stringp == NULL || *stringp == NULL || **stringp == '\0')
+ /* If the string is NULL, there are no tokens to be found. */
+ if (stringp == NULL || *stringp == NULL)
return NULL;
/*
@@ -271,33 +271,19 @@
return ret;
}
- char *found;
- while (**stringp != '\0') {
- found = strstr(*stringp, delim);
-
- if (!found) {
- /*
- * The delimiter was not found, so the full string
- * makes up the only token, and we're done.
- */
- ret = *stringp;
- *stringp = NULL;
- break;
- }
-
- if (found != *stringp) {
- /* There's a non-empty token before the delimiter. */
- *found = '\0';
- ret = *stringp;
- *stringp = found + strlen(delim);
- break;
- }
-
+ char *found = strstr(*stringp, delim);
+ if (!found) {
/*
- * The delimiter was found at the start of the string,
- * skip it and keep looking for a non-empty token.
+ * The delimiter was not found, so the full string
+ * makes up the only token, and we're done.
*/
- *stringp += strlen(delim);
+ ret = *stringp;
+ *stringp = NULL;
+ } else {
+ /* There's a token here, possibly empty. That's OK. */
+ *found = '\0';
+ ret = *stringp;
+ *stringp = found + strlen(delim);
}
return ret;
diff --git a/util.h b/util.h
index 9ec88ce..7ff86b8 100644
--- a/util.h
+++ b/util.h
@@ -83,6 +83,18 @@
int parse_size(size_t *size, const char *sizespec);
char *strip(char *s);
+
+/*
+ * tokenize: locate the next token in @stringp using the @delim
+ * @stringp A pointer to the string to scan for tokens
+ * @delim The delimiter to split by
+ *
+ * Note that, unlike strtok, @delim is not a set of characters, but the full
+ * delimiter. e.g. "a,;b,;c" with a delim of ",;" will yield ["a","b","c"].
+ *
+ * Note that, unlike strtok, this may return an empty token. e.g. "a,,b" with
+ * strtok will yield ["a","b"], but this will yield ["a","","b"].
+ */
char *tokenize(char **stringp, const char *delim);
char *path_join(const char *external_path, const char *internal_path);
diff --git a/util_unittest.cc b/util_unittest.cc
new file mode 100644
index 0000000..ec3d714
--- /dev/null
+++ b/util_unittest.cc
@@ -0,0 +1,93 @@
+// util_unittest.cpp
+// Copyright (C) 2018 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.
+//
+// Test system.[ch] module code using gtest.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include "util.h"
+
+// Sanity check for the strip func.
+TEST(strip, basic) {
+ char str[] = " foo\t";
+ ASSERT_EQ("foo", std::string(strip(str)));
+}
+
+// Make sure we don't crash with various "null"-like inputs.
+TEST(tokenize, null_stringp) {
+ ASSERT_EQ(nullptr, tokenize(nullptr, nullptr));
+ ASSERT_EQ(nullptr, tokenize(nullptr, ""));
+ ASSERT_EQ(nullptr, tokenize(nullptr, ","));
+
+ char *p = nullptr;
+ ASSERT_EQ(nullptr, tokenize(&p, nullptr));
+}
+
+// Make sure we don't crash with various "null"-like inputs.
+TEST(tokenize, null_delim) {
+ char str[] = "a,b,c";
+ char *p = str;
+ ASSERT_EQ(str, tokenize(&p, nullptr));
+ ASSERT_EQ(nullptr, p);
+ ASSERT_EQ(str, std::string("a,b,c"));
+
+ p = str;
+ ASSERT_EQ(str, tokenize(&p, ""));
+ ASSERT_EQ(nullptr, p);
+ ASSERT_EQ(str, std::string("a,b,c"));
+}
+
+// Sanity check for the tokenize func.
+TEST(tokenize, basic) {
+ char str[] = "a,b,c";
+ char *p = str;
+ ASSERT_EQ("a", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("b", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("c", std::string(tokenize(&p, ",")));
+ ASSERT_EQ(nullptr, p);
+ ASSERT_EQ(nullptr, tokenize(&p, ","));
+}
+
+// Check edge case with an empty string.
+TEST(tokenize, empty_string) {
+ char str[] = "";
+ char *p = str;
+ ASSERT_EQ("", std::string(tokenize(&p, ",")));
+ ASSERT_EQ(nullptr, p);
+ ASSERT_EQ(nullptr, tokenize(&p, ","));
+}
+
+// Check behavior with empty tokens at the start/middle/end.
+TEST(tokenize, empty_tokens) {
+ char str[] = ",,a,b,,,c,,";
+ char *p = str;
+ ASSERT_EQ("", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("a", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("b", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("c", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("", std::string(tokenize(&p, ",")));
+ ASSERT_EQ("", std::string(tokenize(&p, ",")));
+ ASSERT_EQ(nullptr, p);
+ ASSERT_EQ(nullptr, tokenize(&p, ","));
+}