-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
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
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
{{"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"},
.loglevel = INFO,
.daemonize = false,
.tlimit = 0,
+ .max_cpu_num = 0,
.keep_caps = false,
.disable_no_new_privs = false,
.rl_as = 512 * (1024 * 1024),
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;
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;
#include <unistd.h>
#include "cgroup.h"
+#include "cpu.h"
#include "log.h"
#include "mount.h"
#include "net.h"
return mountInitNs(nsjconf);
}
+static bool containCPU(struct nsjconf_t *nsjconf)
+{
+ return cpuInit(nsjconf);
+}
+
static bool containSetLimits(struct nsjconf_t *nsjconf)
{
struct rlimit64 rl;
bool containContain(struct nsjconf_t * nsjconf)
{
+ if (containCPU(nsjconf) == false) {
+ return false;
+ }
if (containUserNs(nsjconf) == false) {
return false;
}
--- /dev/null
+/*
+
+ 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;
+}
--- /dev/null
+/*
+
+ 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 */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
}
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;
+}
#define NS_UTIL_H
#include <stdbool.h>
+#include <stdint.h>
#include <stdlib.h>
#include "common.h"
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 */