cmdline: implement affinity setting, to limit jailed process to n max cpus
authorRobert Swiecki <robert@swiecki.net>
Mon, 19 Jun 2017 15:01:50 +0000 (17:01 +0200)
committerRobert Swiecki <robert@swiecki.net>
Mon, 19 Jun 2017 15:01:50 +0000 (17:01 +0200)
Makefile
cmdline.c
common.h
contain.c
cpu.c [new file with mode: 0644]
cpu.h [new file with mode: 0644]
util.c
util.h

index 67a1992834d5a863835a7c2f4a70e8d4dfc541cc..437e75540e87bd4577381d67b1e728b2f09dd2ec 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -28,11 +28,11 @@ CFLAGS += -O2 -c -std=gnu11 \
        -Wall -Wextra -Werror \
        -Ikafel/include
 
-LDFLAGS += -Wl,-z,now -Wl,-z,relro -pie -Wl,-z,noexecstack
+LDFLAGS += -Wl,-z,now -Wl,-z,relro -pie -Wl,-z,noexecstack -lpthread
 
 BIN = nsjail
 LIBS = kafel/libkafel.a
-SRCS = nsjail.c cmdline.c config.c contain.c log.c cgroup.c mount.c net.c pid.c sandbox.c subproc.c user.c util.c uts.c
+SRCS = nsjail.c cmdline.c config.c contain.c log.c cgroup.c mount.c net.c pid.c sandbox.c subproc.c user.c util.c uts.c cpu.c
 OBJS = $(SRCS:.c=.o)
 
 ifdef DEBUG
@@ -139,7 +139,7 @@ indent:
 nsjail.o: nsjail.h common.h cmdline.h log.h net.h subproc.h
 cmdline.o: cmdline.h common.h config.h log.h mount.h util.h user.h
 config.o: common.h config.h log.h mount.h user.h util.h
-contain.o: contain.h common.h cgroup.h log.h mount.h net.h pid.h user.h
+contain.o: contain.h common.h cgroup.h cpu.h log.h mount.h net.h pid.h user.h
 contain.o: util.h uts.h
 log.o: log.h common.h
 cgroup.o: cgroup.h common.h log.h util.h
@@ -152,3 +152,4 @@ subproc.o: util.h
 user.o: user.h common.h log.h subproc.h util.h
 util.o: util.h common.h log.h
 uts.o: uts.h common.h log.h
+cpu.o: cpu.h common.h log.h util.h
index 4f61380b03ec1776d2cd449b6fc5631145c41f71..ff14f324b66c4d78b4f597934aa3ac7c5bda32d5 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -79,6 +79,7 @@ struct custom_option custom_opts[] = {
     {{"log", required_argument, NULL, 'l'}, "Log file (default: use log_fd)"},
     {{"log_fd", required_argument, NULL, 'L'}, "Log FD (default: 2)"},
     {{"time_limit", required_argument, NULL, 't'}, "Maximum time that a jail can exist, in seconds (default: 600)"},
+    {{"max_cpu_num", required_argument, NULL, 0x508}, "Maximum number of CPUs a single jailed process can use (default: 0 'no limit')"},
     {{"daemon", no_argument, NULL, 'd'}, "Daemonize after start"},
     {{"verbose", no_argument, NULL, 'v'}, "Verbose output"},
     {{"quiet", no_argument, NULL, 'q'}, "Only output warning and more important messages"},
@@ -314,6 +315,7 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
       .loglevel = INFO,
       .daemonize = false,
       .tlimit = 0,
+      .max_cpu_num = 0,
       .keep_caps = false,
       .disable_no_new_privs = false,
       .rl_as = 512 * (1024 * 1024),
@@ -535,6 +537,9 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                case 0x0507:
                        nsjconf->disable_no_new_privs = true;
                        break;
+               case 0x0508:
+                       nsjconf->max_cpu_num = strtoul(optarg, NULL, 0);
+                       break;
                case 0x0601:
                        nsjconf->is_root_rw = true;
                        break;
index f848a7c07dffeab98e8265b04f4d29e21565e0a9..1e8c8d2beb66d96af19071bfcb89a1cfb037ec1b 100644 (file)
--- a/common.h
+++ b/common.h
@@ -124,6 +124,7 @@ struct nsjconf_t {
        enum llevel_t loglevel;
        bool daemonize;
        time_t tlimit;
+       size_t max_cpu_num;
        bool keep_env;
        bool keep_caps;
        bool disable_no_new_privs;
index 2e51a6ac16bac488147af0699ce79ffa9ca1867a..938fd1826a7e86ed63f6c93c7a6c226b0a9dcaf5 100644 (file)
--- a/contain.c
+++ b/contain.c
@@ -43,6 +43,7 @@
 #include <unistd.h>
 
 #include "cgroup.h"
+#include "cpu.h"
 #include "log.h"
 #include "mount.h"
 #include "net.h"
@@ -168,6 +169,11 @@ static bool containInitMountNs(struct nsjconf_t *nsjconf)
        return mountInitNs(nsjconf);
 }
 
+static bool containCPU(struct nsjconf_t *nsjconf)
+{
+       return cpuInit(nsjconf);
+}
+
 static bool containSetLimits(struct nsjconf_t *nsjconf)
 {
        struct rlimit64 rl;
@@ -351,6 +357,9 @@ bool containSetupFD(struct nsjconf_t * nsjconf, int fd_in, int fd_out, int fd_er
 
 bool containContain(struct nsjconf_t * nsjconf)
 {
+       if (containCPU(nsjconf) == false) {
+               return false;
+       }
        if (containUserNs(nsjconf) == false) {
                return false;
        }
diff --git a/cpu.c b/cpu.c
new file mode 100644 (file)
index 0000000..ba3f417
--- /dev/null
+++ b/cpu.c
@@ -0,0 +1,84 @@
+/*
+
+   nsjail - CLONE_NEWUTS routines
+   -----------------------------------------
+
+   Copyright 2014 Google Inc. All Rights Reserved.
+
+   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 "cpu.h"
+
+#include <sched.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "util.h"
+
+static void cpuSetRandomCpu(cpu_set_t * mask, size_t mask_size, size_t cpu_num)
+{
+       if ((size_t) CPU_COUNT_S(mask_size, mask) >= cpu_num) {
+               LOG_F
+                   ("Number of CPUs in the mask '%d' is bigger than number of available CPUs '%zu'",
+                    CPU_COUNT(mask), cpu_num);
+       }
+
+       for (;;) {
+               uint64_t n = utilRnd64() % cpu_num;
+               if (!CPU_ISSET_S(n, mask_size, mask)) {
+                       CPU_SET_S(n, mask_size, mask);
+                       break;
+               }
+       }
+}
+
+bool cpuInit(struct nsjconf_t *nsjconf)
+{
+       long all_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+       if (all_cpus < 0) {
+               PLOG_W("sysconf(_SC_NPROCESSORS_ONLN) returned %ld", all_cpus);
+               return false;
+       }
+       if (nsjconf->max_cpu_num >= (size_t) all_cpus) {
+               LOG_D("Requested number of CPUs '%zu' is bigger that CPUs online '%ld'",
+                     nsjconf->max_cpu_num, all_cpus);
+               return true;
+       }
+       if (nsjconf->max_cpu_num == 0) {
+               LOG_D("No max_cpu_num limit set");
+               return true;
+       }
+
+       cpu_set_t *mask = CPU_ALLOC(all_cpus);
+       if (mask == NULL) {
+               PLOG_W("Failure allocating cpu_set_t for %ld CPUs", all_cpus);
+               return false;
+       }
+
+       size_t mask_size = CPU_ALLOC_SIZE(all_cpus);
+       CPU_ZERO_S(mask_size, mask);
+
+       for (size_t i = 0; i < nsjconf->max_cpu_num; i++) {
+               cpuSetRandomCpu(mask, mask_size, all_cpus);
+       }
+
+       if (sched_setaffinity(0, mask_size, mask) == -1) {
+               PLOG_W("sched_setaffinity(max_cpu_num=%zu) failed", nsjconf->max_cpu_num);
+               return false;
+       }
+
+       return true;
+}
diff --git a/cpu.h b/cpu.h
new file mode 100644 (file)
index 0000000..fb59171
--- /dev/null
+++ b/cpu.h
@@ -0,0 +1,32 @@
+/*
+
+   nsjail - CPU affinity
+   -----------------------------------------
+
+   Copyright 2014 Google Inc. All Rights Reserved.
+
+   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.
+
+*/
+
+#ifndef NS_CPU_H
+#define NS_CPU_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "common.h"
+
+bool cpuInit(struct nsjconf_t *nsjconf);
+
+#endif                         /* NS_CPU_H */
diff --git a/util.c b/util.c
index 0132d7c54fc6c1f920924227b1ca640118de9341..37b9e862d7d88039db8e88c692f8c68dbfe0c0bc 100644 (file)
--- a/util.c
+++ b/util.c
@@ -24,6 +24,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <pthread.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -213,3 +214,30 @@ bool utilIsANumber(const char *s)
        }
        return true;
 }
+
+static __thread pthread_once_t rndThreadOnce = PTHREAD_ONCE_INIT;
+static __thread uint64_t rndX;
+
+/* MMIX LCG PRNG */
+static const uint64_t a = 6364136223846793005ULL;
+static const uint64_t c = 1442695040888963407ULL;
+
+static void utilRndInitThread(void)
+{
+       int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+       if (fd == -1) {
+               PLOG_F("Couldn't open /dev/urandom for reading");
+       }
+       if (utilReadFromFd(fd, (uint8_t *) & rndX, sizeof(rndX)) != sizeof(rndX)) {
+               PLOG_F("Couldn't read '%zu' bytes from /dev/urandom", sizeof(rndX));
+               close(fd);
+       }
+       close(fd);
+}
+
+uint64_t utilRnd64(void)
+{
+       pthread_once(&rndThreadOnce, utilRndInitThread);
+       rndX = a * rndX + c;
+       return rndX;
+}
diff --git a/util.h b/util.h
index 2015f215259314bf5004021e5526327438e3927b..e65ab406b286db3c531395479c17cf0b7aa9d4fa 100644 (file)
--- a/util.h
+++ b/util.h
@@ -23,6 +23,7 @@
 #define NS_UTIL_H
 
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
 
 #include "common.h"
@@ -38,5 +39,6 @@ bool utilWriteBufToFile(const char *filename, const void *buf, size_t len, int o
 bool utilCreateDirRecursively(const char *dir);
 int utilSSnPrintf(char *str, size_t size, const char *format, ...);
 bool utilIsANumber(const char *s);
+uint64_t utilRnd64(void);
 
 #endif                         /* NS_UTIL_H */