Improve the way uid/gid changes in unprivileged userns
This change uses whatever was passed into the -u/-g flags as the user to change
in the user namespace. This is used to fix an issue where calling open(2) on a
file on the tmpfs created by minijail would return EOVERFLOW[1]. An easy way to
reproduce is running this on a 4.8 kernel (or Ubuntu Xenial, which has this
change backported):
$ ./minijail0 -T static -Ut -- /bin/bash -c 'touch /tmp/foo'
This change allows a non-zero uid/gid to be mapped to the current user when
entering a namespace, to avoid the above issue.
1: More information about the bug here:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087
Bug: None
Test: make tests
Test: ./minijail0 -T static -Ut -u 1000 -g 1000 -M -m -- \
/bin/bash -c 'touch /tmp/foo'
Change-Id: I393daaf8c2b2355e33c75a908345bb03f1980271
diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc
index 77775b5..93e3790 100644
--- a/libminijail_unittest.cc
+++ b/libminijail_unittest.cc
@@ -358,6 +358,52 @@
minijail_destroy(j);
}
+TEST(Test,
+#if defined(__ANDROID__)
+// TODO(lhchavez): Android unit tests don't currently support entering user
+// namespaces as unprivileged users.
+DISABLED_test_tmpfs_userns
+#else
+test_tmpfs_userns
+#endif
+) {
+ int mj_run_ret;
+ int status;
+ char *argv[4];
+ char uidmap[128], gidmap[128];
+ constexpr uid_t kTargetUid = 1000; // Any non-zero value will do.
+ constexpr gid_t kTargetGid = 1000;
+
+ struct minijail *j = minijail_new();
+
+ minijail_namespace_pids(j);
+ minijail_namespace_vfs(j);
+ minijail_mount_tmp(j);
+ minijail_run_as_init(j);
+
+ // Perform userns mapping.
+ minijail_namespace_user(j);
+ snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid());
+ snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid());
+ minijail_change_uid(j, kTargetUid);
+ minijail_change_gid(j, kTargetGid);
+ minijail_uidmap(j, uidmap);
+ minijail_gidmap(j, gidmap);
+ minijail_namespace_user_disable_setgroups(j);
+
+ argv[0] = (char*)kShellPath;
+ argv[1] = "-c";
+ argv[2] = "exec touch /tmp/foo";
+ argv[3] = NULL;
+ mj_run_ret = minijail_run_no_preload(j, argv[0], argv);
+ EXPECT_EQ(mj_run_ret, 0);
+
+ status = minijail_wait(j);
+ EXPECT_EQ(status, 0);
+
+ minijail_destroy(j);
+}
+
TEST(Test, parse_size) {
size_t size;