Add the ability to set rlimits on the jailed process

Currently Chrome OS relies on upstart to configure these limits but
that isn't available when using libminijail from session manager.  Add
it so runtime limits can be configured for Android and other containers.

BUG=none
TEST=updated security_Minijail0 and manually check /proc/xxx/limits of
jailed process.

Change-Id: I62ed63c89c9c5196b7d9873520b396c9524e5855
Signed-off-by: Dylan Reid <dgreid@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index 81cea4b..b119542 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -25,6 +25,7 @@
 #include <sys/mount.h>
 #include <sys/param.h>
 #include <sys/prctl.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/user.h>
@@ -74,9 +75,17 @@
 
 #define MAX_CGROUPS 10 /* 10 different controllers supported by Linux. */
 
+#define MAX_RLIMITS 32 /* Currently there are 15 supported by Linux. */
+
 /* Keyctl commands. */
 #define KEYCTL_JOIN_SESSION_KEYRING 1
 
+struct minijail_rlimit {
+	int type;
+	uint32_t cur;
+	uint32_t max;
+};
+
 struct mountpoint {
 	char *src;
 	char *dest;
@@ -155,6 +164,8 @@
 	size_t tmpfs_size;
 	char *cgroups[MAX_CGROUPS];
 	size_t cgroup_count;
+	struct minijail_rlimit rlimits[MAX_RLIMITS];
+	size_t rlimit_count;
 };
 
 /*
@@ -639,6 +650,26 @@
 	return 0;
 }
 
+int API minijail_rlimit(struct minijail *j, int type, uint32_t cur,
+			uint32_t max)
+{
+	size_t i;
+
+	if (j->rlimit_count >= MAX_RLIMITS)
+		return -ENOMEM;
+	/* It's an error if the caller sets the same rlimit multiple times. */
+	for (i = 0; i < j->rlimit_count; i++) {
+		if (j->rlimits[i].type == type)
+			return -EEXIST;
+	}
+
+	j->rlimits[j->rlimit_count].type = type;
+	j->rlimits[j->rlimit_count].cur = cur;
+	j->rlimits[j->rlimit_count].max = max;
+	j->rlimit_count++;
+	return 0;
+}
+
 int API minijail_forward_signals(struct minijail *j)
 {
 	j->flags.forward_signals = 1;
@@ -1307,6 +1338,19 @@
 	}
 }
 
+static void set_rlimits_or_die(const struct minijail *j)
+{
+	size_t i;
+
+	for (i = 0; i < j->rlimit_count; ++i) {
+		struct rlimit limit;
+		limit.rlim_cur = j->rlimits[i].cur;
+		limit.rlim_max = j->rlimits[i].max;
+		if (prlimit(j->initpid, j->rlimits[i].type, &limit, NULL))
+			kill_child_and_die(j, "failed to set rlimit");
+	}
+}
+
 static void write_ugid_maps_or_die(const struct minijail *j)
 {
 	if (j->uidmap && write_proc_file(j->initpid, j->uidmap, "uid_map") != 0)
@@ -2118,6 +2162,9 @@
 		if (j->flags.cgroups)
 			add_to_cgroups_or_die(j);
 
+		if (j->rlimit_count)
+			set_rlimits_or_die(j);
+
 		if (j->flags.userns)
 			write_ugid_maps_or_die(j);