mirror of
https://github.com/QingdaoU/Judger.git
synced 2024-12-29 16:31:42 +00:00
使用clone代替fork,为使用namespace隔离做准备。
This commit is contained in:
parent
bee4a48784
commit
9b9444fb20
276
runner.c
276
runner.c
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user