使用clone代替fork,为使用namespace隔离做准备。

This commit is contained in:
LiYang 2016-06-27 23:30:34 +08:00
parent bee4a48784
commit 9b9444fb20
2 changed files with 154 additions and 128 deletions

276
runner.c
View File

@ -1,3 +1,4 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@ -10,6 +11,7 @@
#include <signal.h>
#include <errno.h>
#include <pwd.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
@ -18,6 +20,8 @@
#include "runner.h"
#include "logger.h"
#define STACK_SIZE (2 * 1024 * 1024)
int set_timer(int sec, int ms, int is_cpu_time) {
struct itimerval time_val;
@ -31,14 +35,12 @@ int set_timer(int sec, int ms, int is_cpu_time) {
}
void run(struct config *config, struct result *result) {
int status;
struct rusage resource_usage;
struct timeval start, end;
int child_process(void *clone_args){
FILE *log_fp = ((struct clone_args *)clone_args)->log_fp;
struct config *config = ((struct clone_args *)clone_args)->config;
FILE *in_file = NULL, *out_file = NULL, *err_file = NULL;
struct rlimit memory_limit, cpu_time_rlimit;
int signal;
FILE *log_fp = NULL, *in_file = NULL, *out_file = NULL, *err_file = NULL;
#ifndef __APPLE__
#ifndef __APPLE__
int i;
int syscalls_whitelist[] = {SCMP_SYS(read), SCMP_SYS(fstat),
SCMP_SYS(mmap), SCMP_SYS(mprotect),
@ -48,7 +50,134 @@ void run(struct config *config, struct result *result) {
SCMP_SYS(close)};
int syscalls_whitelist_length = sizeof(syscalls_whitelist) / sizeof(int);
scmp_filter_ctx ctx = NULL;
#endif
#endif
// child process
// On success, these system calls return 0.
// On error, -1 is returned, and errno is set appropriately.
if (config->max_memory != MEMORY_UNLIMITED) {
memory_limit.rlim_cur = memory_limit.rlim_max = (rlim_t) (config->max_memory) * 2;
if (setrlimit(RLIMIT_AS, &memory_limit) == -1) {
LOG_FATAL(log_fp, "setrlimit memory failed, errno: %d", errno);
ERROR(log_fp, SETRLIMIT_FAILED);
}
}
if (config->max_cpu_time != CPU_TIME_UNLIMITED) {
// cpu time
if (set_timer(config->max_cpu_time / 1000, config->max_cpu_time % 1000, 1) != SUCCESS) {
LOG_FATAL(log_fp, "set cpu time timer failed");
ERROR(log_fp, SETITIMER_FAILED);
}
// real time
if (set_timer(config->max_real_time / 1000, config->max_real_time % 1000, 0) != SUCCESS) {
LOG_FATAL(log_fp, "set real time timer failed");
ERROR(log_fp, SETITIMER_FAILED);
}
// child process can not inherit timeout rules from parent process defined by setitimer, so we use setrlimit to
// control child process max running time
cpu_time_rlimit.rlim_cur = cpu_time_rlimit.rlim_max = (config->max_cpu_time + 1000) / 1000;
if (setrlimit(RLIMIT_CPU, &cpu_time_rlimit) == -1) {
LOG_FATAL(log_fp, "setrlimit cpu time failed, errno: %d", errno);
ERROR(log_fp, SETRLIMIT_FAILED);
}
}
// read stdin from in file
// On success, these system calls return the new descriptor.
// On error, -1 is returned, and errno is set appropriately.
if (config->in_file != NULL) {
if ((in_file = fopen(config->in_file, "r")) == NULL) {
LOG_FATAL(log_fp, "failed to open stdin redirect file");
ERROR(log_fp, DUP2_FAILED);
}
if (dup2(fileno(in_file), fileno(stdin)) == -1) {
LOG_FATAL(log_fp, "dup2 stdin failed, errno: %d", errno);
ERROR(log_fp, DUP2_FAILED);
}
}
// write stdout to out file
if (config->out_file != NULL) {
if ((out_file = fopen(config->out_file, "w")) == NULL) {
LOG_FATAL(log_fp, "failed to open stdout redirect file");
ERROR(log_fp, DUP2_FAILED);
}
if (dup2(fileno(out_file), fileno(stdout)) == -1) {
LOG_FATAL(log_fp, "dup2 stdout failed, errno: %d", errno);
ERROR(log_fp, DUP2_FAILED);
}
}
// write stderr to err file
if (config->err_file != NULL) {
// if err_file and out_file are the same path, we use out_file pointer as err_file pointer, to avoid conflict
if (strcmp(config->out_file, config->err_file) == 0) {
err_file = out_file;
}
else {
if ((err_file = fopen(config->err_file, "w")) == NULL) {
LOG_FATAL(log_fp, "failed to open stderr redirect file");
ERROR(log_fp, DUP2_FAILED);
}
}
if (dup2(fileno(err_file), fileno(stderr)) == -1) {
LOG_FATAL(log_fp, "dup2 stdout failed, errno: %d", errno);
ERROR(log_fp, DUP2_FAILED);
}
}
if (config->gid != -1 && setgid(config->gid) == -1) {
LOG_FATAL(log_fp, "setgid failed, errno: %d", errno);
ERROR(log_fp, SET_GID_FAILED);
}
if (config->uid != -1 && setuid(config->uid) == -1) {
LOG_FATAL(log_fp, "setuid failed, errno: %d", errno);
ERROR(log_fp, SET_UID_FAILED);
}
#ifndef __APPLE__
if (config->use_sandbox != 0) {
// load seccomp rules
ctx = seccomp_init(SCMP_ACT_KILL);
if (!ctx) {
LOG_FATAL(log_fp, "init seccomp failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
for (i = 0; i < syscalls_whitelist_length; i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls_whitelist[i], 0) != 0) {
LOG_FATAL(log_fp, "load syscall white list failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
}
// add extra rule for execve
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 1, SCMP_A0(SCMP_CMP_EQ, config->path)) != 0) {
LOG_FATAL(log_fp, "load execve rule failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
// only fd 0 1 2 are allowed
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_LE, 2)) != 0) {
LOG_FATAL(log_fp, "load dup2 rule failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
if (seccomp_load(ctx) != 0) {
LOG_FATAL(log_fp, "seccomp load failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
seccomp_release(ctx);
}
#endif
execve(config->path, config->args, config->env);
LOG_FATAL(log_fp, "execve failed, errno: %d", errno);
ERROR(log_fp, EXCEVE_FAILED);
return 1;
}
void run(struct config *config, struct result *result) {
int status;
struct rusage resource_usage;
struct timeval start, end;
int signal, pid;
FILE *log_fp = NULL;
char *stack = NULL;
struct clone_args clone_args;
log_fp = log_open(config->log_path);
if(log_fp == NULL){
@ -70,8 +199,16 @@ void run(struct config *config, struct result *result) {
log_close(log_fp);
return;
}
if((stack = malloc(STACK_SIZE)) == NULL) {
LOG_FATAL(log_fp, "malloc stack failed");
result->flag = SYSTEM_ERROR;
log_close(log_fp);
return;
}
pid_t pid = fork();
clone_args.config = config;
clone_args.log_fp = log_fp;
pid = clone(child_process, stack + STACK_SIZE, SIGCHLD, (void *)(&clone_args));
if (pid < 0) {
LOG_FATAL(log_fp, "fork failed");
@ -79,8 +216,7 @@ void run(struct config *config, struct result *result) {
log_close(log_fp);
return;
}
if (pid > 0) {
else {
// parent process
// on success, returns the process ID of the child whose state has changed;
@ -147,122 +283,6 @@ void run(struct config *config, struct result *result) {
}
gettimeofday(&end, NULL);
result->real_time = (int) (end.tv_sec * 1000 + end.tv_usec / 1000 - start.tv_sec * 1000 - start.tv_usec / 1000);
log_close(log_fp);
}
else {
// child process
// On success, these system calls return 0.
// On error, -1 is returned, and errno is set appropriately.
if (config->max_memory != MEMORY_UNLIMITED) {
memory_limit.rlim_cur = memory_limit.rlim_max = (rlim_t) (config->max_memory) * 2;
if (setrlimit(RLIMIT_AS, &memory_limit) == -1) {
LOG_FATAL(log_fp, "setrlimit memory failed, errno: %d", errno);
ERROR(log_fp, SETRLIMIT_FAILED);
}
}
if (config->max_cpu_time != CPU_TIME_UNLIMITED) {
// cpu time
if (set_timer(config->max_cpu_time / 1000, config->max_cpu_time % 1000, 1) != SUCCESS) {
LOG_FATAL(log_fp, "set cpu time timer failed");
ERROR(log_fp, SETITIMER_FAILED);
}
// real time
if (set_timer(config->max_real_time / 1000, config->max_real_time % 1000, 0) != SUCCESS) {
LOG_FATAL(log_fp, "set real time timer failed");
ERROR(log_fp, SETITIMER_FAILED);
}
// child process can not inherit timeout rules from parent process defined by setitimer, so we use setrlimit to
// control child process max running time
cpu_time_rlimit.rlim_cur = cpu_time_rlimit.rlim_max = (config->max_cpu_time + 1000) / 1000;
if (setrlimit(RLIMIT_CPU, &cpu_time_rlimit) == -1) {
LOG_FATAL(log_fp, "setrlimit cpu time failed, errno: %d", errno);
ERROR(log_fp, SETRLIMIT_FAILED);
}
}
// read stdin from in file
// On success, these system calls return the new descriptor.
// On error, -1 is returned, and errno is set appropriately.
if (config->in_file != NULL) {
if ((in_file = fopen(config->in_file, "r")) == NULL) {
LOG_FATAL(log_fp, "failed to open stdin redirect file");
ERROR(log_fp, DUP2_FAILED);
}
if (dup2(fileno(in_file), fileno(stdin)) == -1) {
LOG_FATAL(log_fp, "dup2 stdin failed, errno: %d", errno);
ERROR(log_fp, DUP2_FAILED);
}
}
// write stdout to out file
if (config->out_file != NULL) {
if ((out_file = fopen(config->out_file, "w")) == NULL) {
LOG_FATAL(log_fp, "failed to open stdout redirect file");
ERROR(log_fp, DUP2_FAILED);
}
if (dup2(fileno(out_file), fileno(stdout)) == -1) {
LOG_FATAL(log_fp, "dup2 stdout failed, errno: %d", errno);
ERROR(log_fp, DUP2_FAILED);
}
}
// write stderr to err file
if (config->err_file != NULL) {
// if err_file and out_file are the same path, we use out_file pointer as err_file pointer, to avoid conflict
if (strcmp(config->out_file, config->err_file) == 0) {
err_file = out_file;
}
else {
if ((err_file = fopen(config->err_file, "w")) == NULL) {
LOG_FATAL(log_fp, "failed to open stderr redirect file");
ERROR(log_fp, DUP2_FAILED);
}
}
if (dup2(fileno(err_file), fileno(stderr)) == -1) {
LOG_FATAL(log_fp, "dup2 stdout failed, errno: %d", errno);
ERROR(log_fp, DUP2_FAILED);
}
}
if (config->gid != -1 && setgid(config->gid) == -1) {
LOG_FATAL(log_fp, "setgid failed, errno: %d", errno);
ERROR(log_fp, SET_GID_FAILED);
}
if (config->uid != -1 && setuid(config->uid) == -1) {
LOG_FATAL(log_fp, "setuid failed, errno: %d", errno);
ERROR(log_fp, SET_UID_FAILED);
}
#ifndef __APPLE__
if (config->use_sandbox != 0) {
// load seccomp rules
ctx = seccomp_init(SCMP_ACT_KILL);
if (!ctx) {
LOG_FATAL(log_fp, "init seccomp failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
for (i = 0; i < syscalls_whitelist_length; i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls_whitelist[i], 0) != 0) {
LOG_FATAL(log_fp, "load syscall white list failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
}
// add extra rule for execve
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 1, SCMP_A0(SCMP_CMP_EQ, config->path)) != 0) {
LOG_FATAL(log_fp, "load execve rule failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
// only fd 0 1 2 are allowed
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_LE, 2)) != 0) {
LOG_FATAL(log_fp, "load dup2 rule failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
if (seccomp_load(ctx) != 0) {
LOG_FATAL(log_fp, "seccomp load failed");
ERROR(log_fp, LOAD_SECCOMP_FAILED);
}
seccomp_release(ctx);
}
#endif
execve(config->path, config->args, config->env);
LOG_FATAL(log_fp, "execve failed, errno: %d", errno);
ERROR(log_fp, EXCEVE_FAILED);
}
log_close(log_fp);
}

View File

@ -58,4 +58,10 @@ struct config {
void run(struct config *, struct result *);
struct clone_args {
struct config *config;
FILE *log_fp;
};
#endif