cmdline: implement affinity setting, to limit jailed process to n max cpus

This commit is contained in:
Robert Swiecki 2017-06-19 17:01:50 +02:00
parent dbdeba6ea4
commit 0e7393cccf
8 changed files with 165 additions and 3 deletions

View File

@ -28,11 +28,11 @@ CFLAGS += -O2 -c -std=gnu11 \
-Wall -Wextra -Werror \ -Wall -Wextra -Werror \
-Ikafel/include -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 BIN = nsjail
LIBS = kafel/libkafel.a 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) OBJS = $(SRCS:.c=.o)
ifdef DEBUG ifdef DEBUG
@ -139,7 +139,7 @@ indent:
nsjail.o: nsjail.h common.h cmdline.h log.h net.h subproc.h 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 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 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 contain.o: util.h uts.h
log.o: log.h common.h log.o: log.h common.h
cgroup.o: cgroup.h common.h log.h util.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 user.o: user.h common.h log.h subproc.h util.h
util.o: util.h common.h log.h util.o: util.h common.h log.h
uts.o: uts.h common.h log.h uts.o: uts.h common.h log.h
cpu.o: cpu.h common.h log.h util.h

View File

@ -79,6 +79,7 @@ struct custom_option custom_opts[] = {
{{"log", required_argument, NULL, 'l'}, "Log file (default: use log_fd)"}, {{"log", required_argument, NULL, 'l'}, "Log file (default: use log_fd)"},
{{"log_fd", required_argument, NULL, 'L'}, "Log FD (default: 2)"}, {{"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)"}, {{"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"}, {{"daemon", no_argument, NULL, 'd'}, "Daemonize after start"},
{{"verbose", no_argument, NULL, 'v'}, "Verbose output"}, {{"verbose", no_argument, NULL, 'v'}, "Verbose output"},
{{"quiet", no_argument, NULL, 'q'}, "Only output warning and more important messages"}, {{"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, .loglevel = INFO,
.daemonize = false, .daemonize = false,
.tlimit = 0, .tlimit = 0,
.max_cpu_num = 0,
.keep_caps = false, .keep_caps = false,
.disable_no_new_privs = false, .disable_no_new_privs = false,
.rl_as = 512 * (1024 * 1024), .rl_as = 512 * (1024 * 1024),
@ -535,6 +537,9 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
case 0x0507: case 0x0507:
nsjconf->disable_no_new_privs = true; nsjconf->disable_no_new_privs = true;
break; break;
case 0x0508:
nsjconf->max_cpu_num = strtoul(optarg, NULL, 0);
break;
case 0x0601: case 0x0601:
nsjconf->is_root_rw = true; nsjconf->is_root_rw = true;
break; break;

View File

@ -124,6 +124,7 @@ struct nsjconf_t {
enum llevel_t loglevel; enum llevel_t loglevel;
bool daemonize; bool daemonize;
time_t tlimit; time_t tlimit;
size_t max_cpu_num;
bool keep_env; bool keep_env;
bool keep_caps; bool keep_caps;
bool disable_no_new_privs; bool disable_no_new_privs;

View File

@ -43,6 +43,7 @@
#include <unistd.h> #include <unistd.h>
#include "cgroup.h" #include "cgroup.h"
#include "cpu.h"
#include "log.h" #include "log.h"
#include "mount.h" #include "mount.h"
#include "net.h" #include "net.h"
@ -168,6 +169,11 @@ static bool containInitMountNs(struct nsjconf_t *nsjconf)
return mountInitNs(nsjconf); return mountInitNs(nsjconf);
} }
static bool containCPU(struct nsjconf_t *nsjconf)
{
return cpuInit(nsjconf);
}
static bool containSetLimits(struct nsjconf_t *nsjconf) static bool containSetLimits(struct nsjconf_t *nsjconf)
{ {
struct rlimit64 rl; 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) bool containContain(struct nsjconf_t * nsjconf)
{ {
if (containCPU(nsjconf) == false) {
return false;
}
if (containUserNs(nsjconf) == false) { if (containUserNs(nsjconf) == false) {
return false; return false;
} }

84
cpu.c Normal file
View File

@ -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;
}

32
cpu.h Normal file
View File

@ -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 */

28
util.c
View File

@ -24,6 +24,7 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -213,3 +214,30 @@ bool utilIsANumber(const char *s)
} }
return true; 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;
}

2
util.h
View File

@ -23,6 +23,7 @@
#define NS_UTIL_H #define NS_UTIL_H
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include "common.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); bool utilCreateDirRecursively(const char *dir);
int utilSSnPrintf(char *str, size_t size, const char *format, ...); int utilSSnPrintf(char *str, size_t size, const char *format, ...);
bool utilIsANumber(const char *s); bool utilIsANumber(const char *s);
uint64_t utilRnd64(void);
#endif /* NS_UTIL_H */ #endif /* NS_UTIL_H */