From 1a7aadef82fc291dbaef7a8be9a4a61209555836 Mon Sep 17 00:00:00 2001 From: virusdefender Date: Mon, 25 Jul 2016 11:19:22 +0800 Subject: [PATCH] new version --- LICENSE | 37 ----- README.md | 34 ----- demo/demo.c | 34 ----- demo/demo.py | 49 ------- demo/in | 1 - judger.c | 150 --------------------- logger.h | 89 ------------ runner.c | 290 ---------------------------------------- runner.h | 67 ---------- setup.py | 14 -- src/child.c | 157 ++++++++++++++++++++++ src/child.h | 16 +++ src/killer.c | 33 +++++ src/killer.h | 13 ++ src/logger.c | 66 +++++++++ src/logger.h | 21 +++ src/rules/Python/rule.c | 8 ++ src/rules/c_cpp/rule.c | 39 ++++++ src/runner.c | 126 +++++++++++++++++ src/runner.h | 80 +++++++++++ test/main.c | 30 +++++ test/test_helper.h | 57 ++++++++ tests/1/Main.c | 15 --- tests/1/config | 1 - tests/1/in | 1 - tests/1/out | 2 - tests/1/result | 1 - tests/10/Main.c | 11 -- tests/10/config | 1 - tests/10/in | 0 tests/10/out | 0 tests/10/result | 1 - tests/11/Main.c | 11 -- tests/11/config | 1 - tests/11/in | 0 tests/11/out | 0 tests/11/result | 1 - tests/12/Main.c | 18 --- tests/12/config | 1 - tests/12/in | 0 tests/12/out | 0 tests/12/result | 1 - tests/13/Main.c | 18 --- tests/13/config | 1 - tests/13/in | 0 tests/13/out | 0 tests/13/result | 1 - tests/14/Main.c | 15 --- tests/14/config | 1 - tests/14/in | 1 - tests/14/out | 2 - tests/14/result | 1 - tests/15/Main.c | 15 --- tests/15/config | 1 - tests/15/in | 1 - tests/15/out | 2 - tests/15/result | 1 - tests/16/Main.c | 12 -- tests/16/config | 1 - tests/16/in | 0 tests/16/out | 1 - tests/16/result | 1 - tests/17/Main.c | 29 ---- tests/17/config | 1 - tests/17/in | 0 tests/17/out | 0 tests/17/result | 1 - tests/18/Main.c | 8 -- tests/18/config | 1 - tests/18/in | 0 tests/18/out | 1 - tests/18/result | 1 - tests/2/Main.c | 36 ----- tests/2/config | 1 - tests/2/in | 0 tests/2/out | 2 - tests/2/result | 1 - tests/3/Main.c | 21 --- tests/3/config | 1 - tests/3/in | 0 tests/3/out | 0 tests/3/result | 1 - tests/4/Main.c | 14 -- tests/4/config | 1 - tests/4/in | 0 tests/4/out | 0 tests/4/result | 1 - tests/5/Main.c | 26 ---- tests/5/config | 1 - tests/5/in | 0 tests/5/out | 0 tests/5/result | 1 - tests/6/Main.c | 17 --- tests/6/config | 1 - tests/6/in | 0 tests/6/out | 0 tests/6/result | 1 - tests/7/Main.c | 13 -- tests/7/config | 1 - tests/7/in | 0 tests/7/out | 0 tests/7/result | 1 - tests/8/Main.c | 12 -- tests/8/config | 1 - tests/8/in | 0 tests/8/out | 3 - tests/8/result | 1 - tests/9/Main.c | 11 -- tests/9/config | 1 - tests/9/in | 0 tests/9/out | 2 - tests/9/result | 1 - tests/test.py | 151 --------------------- 113 files changed, 646 insertions(+), 1272 deletions(-) delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 demo/demo.c delete mode 100644 demo/demo.py delete mode 100644 demo/in delete mode 100644 judger.c delete mode 100644 logger.h delete mode 100644 runner.c delete mode 100644 runner.h delete mode 100644 setup.py create mode 100644 src/child.c create mode 100644 src/child.h create mode 100644 src/killer.c create mode 100644 src/killer.h create mode 100644 src/logger.c create mode 100644 src/logger.h create mode 100644 src/rules/Python/rule.c create mode 100644 src/rules/c_cpp/rule.c create mode 100644 src/runner.c create mode 100644 src/runner.h create mode 100644 test/main.c create mode 100644 test/test_helper.h delete mode 100644 tests/1/Main.c delete mode 100644 tests/1/config delete mode 100644 tests/1/in delete mode 100644 tests/1/out delete mode 100644 tests/1/result delete mode 100644 tests/10/Main.c delete mode 100644 tests/10/config delete mode 100644 tests/10/in delete mode 100644 tests/10/out delete mode 100644 tests/10/result delete mode 100644 tests/11/Main.c delete mode 100644 tests/11/config delete mode 100644 tests/11/in delete mode 100644 tests/11/out delete mode 100644 tests/11/result delete mode 100644 tests/12/Main.c delete mode 100644 tests/12/config delete mode 100644 tests/12/in delete mode 100644 tests/12/out delete mode 100644 tests/12/result delete mode 100644 tests/13/Main.c delete mode 100644 tests/13/config delete mode 100644 tests/13/in delete mode 100644 tests/13/out delete mode 100644 tests/13/result delete mode 100644 tests/14/Main.c delete mode 100644 tests/14/config delete mode 100644 tests/14/in delete mode 100644 tests/14/out delete mode 100644 tests/14/result delete mode 100644 tests/15/Main.c delete mode 100644 tests/15/config delete mode 100644 tests/15/in delete mode 100644 tests/15/out delete mode 100644 tests/15/result delete mode 100644 tests/16/Main.c delete mode 100644 tests/16/config delete mode 100644 tests/16/in delete mode 100644 tests/16/out delete mode 100644 tests/16/result delete mode 100644 tests/17/Main.c delete mode 100644 tests/17/config delete mode 100644 tests/17/in delete mode 100644 tests/17/out delete mode 100644 tests/17/result delete mode 100644 tests/18/Main.c delete mode 100644 tests/18/config delete mode 100644 tests/18/in delete mode 100644 tests/18/out delete mode 100644 tests/18/result delete mode 100644 tests/2/Main.c delete mode 100644 tests/2/config delete mode 100644 tests/2/in delete mode 100644 tests/2/out delete mode 100644 tests/2/result delete mode 100644 tests/3/Main.c delete mode 100644 tests/3/config delete mode 100644 tests/3/in delete mode 100644 tests/3/out delete mode 100644 tests/3/result delete mode 100644 tests/4/Main.c delete mode 100644 tests/4/config delete mode 100644 tests/4/in delete mode 100644 tests/4/out delete mode 100644 tests/4/result delete mode 100644 tests/5/Main.c delete mode 100644 tests/5/config delete mode 100644 tests/5/in delete mode 100644 tests/5/out delete mode 100644 tests/5/result delete mode 100644 tests/6/Main.c delete mode 100644 tests/6/config delete mode 100644 tests/6/in delete mode 100644 tests/6/out delete mode 100644 tests/6/result delete mode 100644 tests/7/Main.c delete mode 100644 tests/7/config delete mode 100644 tests/7/in delete mode 100644 tests/7/out delete mode 100644 tests/7/result delete mode 100644 tests/8/Main.c delete mode 100644 tests/8/config delete mode 100644 tests/8/in delete mode 100644 tests/8/out delete mode 100644 tests/8/result delete mode 100644 tests/9/Main.c delete mode 100644 tests/9/config delete mode 100644 tests/9/in delete mode 100644 tests/9/out delete mode 100644 tests/9/result delete mode 100644 tests/test.py diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ef4d58d..0000000 --- a/LICENSE +++ /dev/null @@ -1,37 +0,0 @@ -The Star And Thank Author License (SATA) - -Copyright (c) - -Project Url: https://github.com/QingdaoU/Judger - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -And wait, the most important, you shall star/+1/like the project(s) in project url -section above first, and then thank the author(s) in Copyright section. - -Here are some suggested ways: - - - Email the authors a thank-you letter, and make friends with him/her/them. - - Report bugs or issues. - - Tell friends what a wonderful project this is. - - And, sure, you can just express thanks in your mind without telling the world. - -Contributors of this project by forking have the option to add his/her name and -forked project url at copyright and project url sections, but shall not delete -or modify anything else in these two sections. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index b836549..0000000 --- a/README.md +++ /dev/null @@ -1,34 +0,0 @@ - -#Judger [![Build Status](https://travis-ci.org/QingdaoU/Judger.svg?branch=master)](https://travis-ci.org/QingdaoU/Judger) - - -OnlineJudge 判题沙箱 - -##Python api 使用方法 - -见 demo - -``` -sudo python setup.py install -cd demo && sudo python demo.py -``` - -##C语言 api 使用方法 - -`#include "runner.c"`即可,然后传入 `config` 和 `result` 的结构体指针。 - - -## 为什么不是ptrace - -ptrace在很多OJ上都有应用,但是不可否认的是ptrace存在一个重大缺点:严重影响进程运行的性能,因为每次系统调用就要进行两次上下文切换,从子进程到父进程,然后父进程到子进程。OJ上题目很多都需要大量的输入和输出,会产生大量的系统调用,导致代码运行时间加长。 - -##注意 - - 本Judger定位于C/C++语言,如果使用Java,由于JVM的特殊性,请设置`use_sandbox=False, max_memory=MEMORY_UNLIMITED`,安全方面使用Java自带的安全策略,内存限制方面使用`Xms`、`Xmx`等参数。参考[这里](https://github.com/QingdaoU/OnlineJudge/blob/master/judge)关于Java的部分注释。 - - `runner.c`里面硬编码了系统调用白名单,在Ubuntu 14.04 64位系统上测试通过。如果在您的系统上正常程序出现了`Runtime Error`可能是部分系统调用不一致导致的。如果怀疑是这个原因提出issue,请务必提供系统版本和`strace ./FILE_NAME`的结果。目前已知32位系统肯定会出现非白名单系统调用,但是因为32位系统无法使用docker,一般出现在本地测试环境中。 - - 如果使用了 `use_nobody = True` 则需要 `root` 权限启动。 - - Python api 请只使用str。 - -##感谢 - - https://github.com/lodevil/Lo-runner - - https://github.com/quark-zju/lrun - - https://github.com/daveho/EasySandbox diff --git a/demo/demo.c b/demo/demo.c deleted file mode 100644 index ab86a23..0000000 --- a/demo/demo.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include - - -int main(int argc, char *argv[]) { - int *p = NULL; - int j; - char input[100]; - // 150M - int v = 150000000; - - printf("start\n"); - - for (j = 0; j < argc; j++) - printf("argv[%d]: %s\n", j, argv[j]); - - scanf("%s", input); - printf("%s\n", input); - - p = (int *) malloc(v); - if (p == NULL) { - printf("malloc failed\n"); - } - else { - memset(p, 0, v); - printf("malloc succeeded\n"); - } - printf("begin to execute command\n"); - system("/bin/ls /tmp"); - printf("uid: %d", getuid()); - return 0; -} diff --git a/demo/demo.py b/demo/demo.py deleted file mode 100644 index a48a39c..0000000 --- a/demo/demo.py +++ /dev/null @@ -1,49 +0,0 @@ -# coding=utf-8 -import os -import judger - -base_path = os.path.dirname(os.path.abspath(__file__)) - - -def _compile(): - return judger.run(path="/usr/bin/gcc", - in_file=os.path.join(base_path, "in"), - out_file=os.path.join(base_path, "gcc_out"), - max_cpu_time=judger.CPU_TIME_UNLIMITED, - max_memory=judger.MEMORY_UNLIMITED, - args=[os.path.join(base_path, "demo.c"), "-o", os.path.join(base_path, "demo")], - env=["PATH=" + os.environ["PATH"]], - log_path="compile.log", - use_sandbox=False, - use_nobody=False) - - -def run(use_sandbox, use_nobody): - print "compile result: ", _compile() - path = os.path.join(base_path, "demo") - return judger.run(path=path, - in_file=os.path.join(base_path, "in"), - out_file=os.path.join(base_path, "out"), - # ms - max_cpu_time=2000, - # Byte - max_memory=200000000, - # args env and log_path are optional - args=["1", "2", "####"], - env=["aaa=123"], - log_path="run.log", - # default is True - use_sandbox=use_sandbox, - use_nobody=use_nobody) - - -print "sandbox and nobody" -print run(use_sandbox=True, use_nobody=True) - -print '\n\nno sandbox and root' -print run(use_sandbox=False, use_nobody=False) -print "\n\nout: ", open("out").read() - -print "\n\nno sandbox and nobody" -print run(use_sandbox=False, use_nobody=True) -print "\n\nout: ", open("out").read() diff --git a/demo/in b/demo/in deleted file mode 100644 index 70c379b..0000000 --- a/demo/in +++ /dev/null @@ -1 +0,0 @@ -Hello world \ No newline at end of file diff --git a/judger.c b/judger.c deleted file mode 100644 index c5c3ffc..0000000 --- a/judger.c +++ /dev/null @@ -1,150 +0,0 @@ -#include -#include -#include -#include -#include "runner.h" - -#define RaiseValueError(msg) {PyErr_SetString(PyExc_ValueError, msg); return NULL;} - - -static PyObject *judger_run(PyObject *self, PyObject *args, PyObject *kwargs) { - struct config config; - struct result result = {0, 0, 0, 0, 0, 0}; - PyObject *args_list = NULL, *env_list = NULL, *use_sandbox = NULL, *use_nobody = NULL, - *next = NULL, *args_iter = NULL, *env_iter = NULL, *log_path = NULL; - int count = 0; - struct passwd *passwd; - static char *kwargs_list[] = {"path", "in_file", "out_file", "max_cpu_time", - "max_memory", "args", "env", "use_sandbox", "use_nobody", "log_path", NULL}; - - config.path = config.in_file = config.out_file = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sssil|OOOOO", kwargs_list, &(config.path), &(config.in_file), - &(config.out_file), &(config.max_cpu_time), &(config.max_memory), - &args_list, &env_list, &use_sandbox, &use_nobody, &log_path)) { - RaiseValueError("Invalid args and kwargs"); - } - if (config.max_cpu_time < 1 && config.max_cpu_time != CPU_TIME_UNLIMITED) { - RaiseValueError("max_cpu_time must > 1 ms or unlimited"); - } - // by default, max_real_time = max_cpu_time * 3 - config.max_real_time = config.max_cpu_time * 3; - if (config.max_memory < 16 * 1024 * 1024 && config.max_memory != MEMORY_UNLIMITED) { - RaiseValueError("max_memory must > 16M or unlimited"); - } - if (access(config.path, F_OK) == -1) { - RaiseValueError("Exec file does not exist"); - } - if (access(config.in_file, F_OK) == -1) { - RaiseValueError("in_file does not exist"); - } - config.err_file = config.out_file; - config.args[count++] = config.path; - if (args_list != NULL) { - if (!PyList_Check(args_list)) { - RaiseValueError("args must be a list"); - } - - args_iter = PyObject_GetIter(args_list); - while (1) { - next = PyIter_Next((args_iter)); - if (!next) { - break; - } - if (!PyString_Check(next) && !PyUnicode_Check(next)) { - RaiseValueError("arg in args must be a string"); - } - config.args[count] = PyString_AsString(next); - count++; - if(count > 95) { - RaiseValueError("Number of args must < 95"); - } - } - } - config.args[count] = NULL; - - count = 0; - if (env_list != NULL) { - if (!PyList_Check(env_list)) { - RaiseValueError("env must be a list"); - } - env_iter = PyObject_GetIter(env_list); - while (1) { - next = PyIter_Next(env_iter); - if (!next) { - break; - } - if (!PyString_Check(next) && !PyUnicode_Check(next)) { - RaiseValueError("env item must be a string"); - } - config.env[count] = PyString_AsString(next); - count++; - if(count > 95) { - RaiseValueError("Number of env must < 95"); - } - } - } - config.env[count] = NULL; - - if (use_sandbox != NULL) { - if (!PyBool_Check(use_sandbox)) { - RaiseValueError("use_sandbox must ba a bool"); - } - config.use_sandbox = PyObject_IsTrue(use_sandbox); - } - else { - config.use_sandbox = 1; - } - - if (use_nobody != NULL) { - if (!PyBool_Check(use_nobody)) { - RaiseValueError("use_nobody must be a bool"); - } - if (PyObject_IsTrue(use_nobody)) { - passwd = getpwnam("nobody"); - if(passwd == NULL) { - RaiseValueError("get nobody user info failed"); - } - config.gid = passwd->pw_gid; - config.uid = passwd->pw_uid; - } - else { - config.uid = config.gid = -1; - } - } - else { - config.uid = config.gid = -1; - } - - if (log_path != NULL) { - if (!PyString_Check(log_path)) { - RaiseValueError("log path must be a string"); - } - config.log_path = PyString_AsString(log_path); - } - else { - config.log_path = "judger.log"; - } - - if((config.uid != -1 || config.gid != -1) && getuid() != 0) { - RaiseValueError("Root user is required when use_nobody=True"); - } - - run(&config, &result); - return Py_BuildValue("{s:l, s:i, s:i, s:i, s:i, s:i}", - "cpu_time", result.cpu_time, "memory", result.memory, "real_time", result.real_time, "signal", - result.signal, "flag", result.flag, "exit_status", result.exit_status); - -} - - -static PyMethodDef judger_methods[] = { - {"run", (PyCFunction) judger_run, METH_KEYWORDS, NULL}, - {NULL, NULL, 0, NULL} -}; - - -PyMODINIT_FUNC initjudger(void) { - PyObject *module = Py_InitModule3("judger", judger_methods, NULL); - PyModule_AddIntConstant(module, "CPU_TIME_UNLIMITED", CPU_TIME_UNLIMITED); - PyModule_AddIntConstant(module, "MEMORY_UNLIMITED", MEMORY_UNLIMITED); -} diff --git a/logger.h b/logger.h deleted file mode 100644 index 8037326..0000000 --- a/logger.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef __LOGGER__ -#define __LOGGER__ - -#include -#include -#include -#include -#include -#include -#include - -FILE* log_open(const char *); -void log_close(FILE*); -static void log_write(int level, const char* source_filename, const int line_number, const FILE* log_fp, const char*, ...); - -const int LOG_FATAL = 0; -const int LOG_WARNING = 1; -const int LOG_INFO = 2; -const int LOG_DEBUG = 3; -static char LOG_LEVEL_NOTE[][10] = - { "FATAL", "WARNING", "INFO", "DEBUG" }; -#define LOG_DEBUG(log_fp, x...) log_write(LOG_DEBUG, __FILE__, __LINE__, log_fp, ##x) -#define LOG_INFO(log_fp, x...) log_write(LOG_INFO, __FILE__ __LINE__, log_fp, ##x) -#define LOG_WARNING(log_fp, x...) log_write(LOG_WARNING, __FILE__, __LINE__, log_fp, ##x) -#define LOG_FATAL(log_fp, x...) log_write(LOG_FATAL, __FILE__, __LINE__, log_fp, ##x) - -#define log_buffer_size 8192 - -FILE* log_open(const char* filename) -{ - FILE* log_fp = fopen(filename, "a"); - if (log_fp == NULL) - { - fprintf(stderr, "can not open log file %s", filename); - } - return log_fp; -} - -void log_close(FILE* log_fp) -{ - if (log_fp != NULL) - { - fclose(log_fp); - } -} - -static void log_write(int level, const char* source_filename, const int line, const FILE* log_fp, const char *fmt, ...) -{ - if (log_fp == NULL) - { - fprintf(stderr, "can not open log file"); - return; - } - static char buffer[log_buffer_size]; - static char log_buffer[log_buffer_size]; - static char datetime[100]; - static char line_str[20]; - static time_t now; - now = time(NULL); - - strftime(datetime, 99, "%Y-%m-%d %H:%M:%S", localtime(&now)); - snprintf(line_str, 19, "%d", line); - va_list ap; - va_start(ap, fmt); - vsnprintf(log_buffer, log_buffer_size, fmt, ap); - va_end(ap); - - size_t count = snprintf(buffer, log_buffer_size, - "%s [%s] [%s:%d]%s\n", - LOG_LEVEL_NOTE[level], datetime, source_filename, line, log_buffer); - fprintf(stdout, "%s", buffer); - int log_fd = fileno((FILE *)log_fp); - if (flock(log_fd, LOCK_EX) == 0) - { - if (write(log_fd, buffer, count) < 0) - { - fprintf(stderr, "write error"); - return; - } - flock(log_fd, LOCK_UN); - } - else - { - fprintf(stderr, "flock error"); - return; - } -} - -#endif diff --git a/runner.c b/runner.c deleted file mode 100644 index 69183b6..0000000 --- a/runner.c +++ /dev/null @@ -1,290 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#ifndef __APPLE__ -#include -#else -#warning "###### This judger can not work under OSX, installation is only for dev dependencies! #####" -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; - time_val.it_interval.tv_sec = time_val.it_interval.tv_usec = 0; - time_val.it_value.tv_sec = sec; - time_val.it_value.tv_usec = ms * 1000; - if (setitimer(is_cpu_time ? ITIMER_VIRTUAL : ITIMER_REAL, &time_val, NULL)) { - return SETITIMER_FAILED; - } - return SUCCESS; -} - - -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; - gid_t group_list[] = {config->gid}; -#ifndef __APPLE__ - int i; - int syscalls_whitelist[] = {SCMP_SYS(read), SCMP_SYS(fstat), - SCMP_SYS(mmap), SCMP_SYS(mprotect), - SCMP_SYS(munmap), SCMP_SYS(open), - SCMP_SYS(arch_prctl), SCMP_SYS(brk), - SCMP_SYS(access), SCMP_SYS(exit_group), - SCMP_SYS(close)}; - int syscalls_whitelist_length = sizeof(syscalls_whitelist) / sizeof(int); - scmp_filter_ctx ctx = NULL; -#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 || setgroups(sizeof(group_list) / sizeof(gid_t), group_list) == -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){ - result->flag = SYSTEM_ERROR; - return; - } - - gettimeofday(&start, NULL); - - if(config->max_memory < 1 && config->max_memory != MEMORY_UNLIMITED) { - LOG_FATAL(log_fp, "max_memory must > 1 or unlimited"); - result->flag = SYSTEM_ERROR; - log_close(log_fp); - return; - } - if(config->max_cpu_time < 1 && config->max_cpu_time != CPU_TIME_UNLIMITED) { - LOG_FATAL(log_fp, "max_cpu_time must > 1 or unlimited"); - result->flag = SYSTEM_ERROR; - 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; - } - - 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"); - result->flag = SYSTEM_ERROR; - log_close(log_fp); - return; - } - else { - // parent process - - // on success, returns the process ID of the child whose state has changed; - // On error, -1 is returned. - if (wait4(pid, &status, 0, &resource_usage) == -1) { - LOG_FATAL(log_fp, "wait4 failed"); - result->flag = SYSTEM_ERROR; - log_close(log_fp); - return; - } - LOG_DEBUG(log_fp, "exit status: %d", WEXITSTATUS(status)); - result->exit_status = WEXITSTATUS(status); - result->cpu_time = (int) (resource_usage.ru_utime.tv_sec * 1000 + - resource_usage.ru_utime.tv_usec / 1000 + - resource_usage.ru_stime.tv_sec * 1000 + - resource_usage.ru_stime.tv_usec / 1000); - // avoid 0 ms - if(result->cpu_time == 0) { - result->cpu_time = 1; - } - - // osx: ru_maxrss the maximum resident set size utilized (in bytes). - // linux: ru_maxrss (since Linux 2.6.32)This is the maximum resident set size used (in kilobytes). - // For RUSAGE_CHILDREN, this is the resident set size of the largest child, - // not the maximum resident set size of the processtree. - result->memory = resource_usage.ru_maxrss * 1024; - - result->signal = 0; - result->flag = SUCCESS; - - if (WIFSIGNALED(status) != 0) { - signal = WTERMSIG(status); - LOG_DEBUG(log_fp, "signal: %d", signal); - result->signal = signal; - if (signal == SIGALRM) { - result->flag = REAL_TIME_LIMIT_EXCEEDED; - } - else if (signal == SIGVTALRM) { - result->flag = CPU_TIME_LIMIT_EXCEEDED; - } - else if (signal == SIGSEGV) { - if (config->max_memory != MEMORY_UNLIMITED && result->memory > config->max_memory) { - result->flag = MEMORY_LIMIT_EXCEEDED; - } - else { - result->flag = RUNTIME_ERROR; - } - } - // Child process error - else if (signal == SIGUSR1){ - result->flag = SYSTEM_ERROR; - } - else { - result->flag = RUNTIME_ERROR; - } - } - else { - if (config->max_memory != MEMORY_UNLIMITED && result->memory > config->max_memory) { - result->flag = MEMORY_LIMIT_EXCEEDED; - } - if (WEXITSTATUS(status) != 0) { - result->flag = RUNTIME_ERROR; - } - } - 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); - } -} diff --git a/runner.h b/runner.h deleted file mode 100644 index 1913d91..0000000 --- a/runner.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef JUDGER_RUNNER_H -#define JUDGER_RUNNER_H - -#define CPU_TIME_UNLIMITED -1 -#define MEMORY_UNLIMITED -1 - - -#define SUCCESS 0 - -#define FORK_FAILED 1 -#define WAIT4_FAILED 2 -#define RUN_FAILED 3 -#define SETITIMER_FAILED 4 -#define SETRLIMIT_FAILED 5 -#define DUP2_FAILED 6 -#define EXCEVE_FAILED 7 -#define LOAD_SECCOMP_FAILED 8 -#define SET_UID_FAILED 9 -#define SET_GID_FAILED 10 - - -#define CPU_TIME_LIMIT_EXCEEDED 1 -#define REAL_TIME_LIMIT_EXCEEDED 2 -#define MEMORY_LIMIT_EXCEEDED 3 -#define RUNTIME_ERROR 4 -#define SYSTEM_ERROR 5 - - -#define ERROR(log_fp, code) LOG_FATAL(log_fp, "judger return error code: %d", code);raise(SIGUSR1) - - -struct result { - int cpu_time; - long memory; - int real_time; - int signal; - int flag; - int exit_status; -}; - - -struct config { - int max_cpu_time; - int max_real_time; - long max_memory; - char *path; - char *in_file; - char *out_file; - char *err_file; - char *args[100]; - char *env[100]; - int use_sandbox; - char *log_path; - uid_t uid; - gid_t gid; -}; - - -void run(struct config *, struct result *); - - -struct clone_args { - struct config *config; - FILE *log_fp; -}; - -#endif diff --git a/setup.py b/setup.py deleted file mode 100644 index 1c92ccc..0000000 --- a/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -# coding=utf-8 -import platform -from distutils.core import setup, Extension - - -if platform.system() != 'Darwin': - libraries = ['seccomp'] -else: - libraries = [] - -setup(name='judger', - version='1.0', - ext_modules=[Extension('judger', sources=['judger.c', 'runner.c'], - libraries=libraries)]) diff --git a/src/child.c b/src/child.c new file mode 100644 index 0000000..a9e81be --- /dev/null +++ b/src/child.c @@ -0,0 +1,157 @@ +#define _BSD_SOURCE +#define _POSIX_SOURCE +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "runner.h" +#include "child.h" +#include "logger.h" + +#include "killer.h" + + +void close_file(FILE *fp, ...) { + va_list args; + va_start(args, fp); + + if (fp != NULL) { + fclose(fp); + } + + va_end(args); +} + + +int child_process(void *args) { + FILE *log_fp = ((child_args *) args)->log_fp; + runner_config *config = ((child_args *) args)->config; + + // set memory limit + if (config->max_memory != UNLIMITED) { + struct rlimit max_memory; + max_memory.rlim_cur = max_memory.rlim_max = (rlim_t) (config->max_memory) * 2; + if (setrlimit(RLIMIT_AS, &max_memory) != 0) { + CHILD_ERROR_EXIT(SETRLIMIT_FAILED); + } + } + + // set cpu time limit (in seconds) + if (config->max_cpu_time != UNLIMITED) { + struct rlimit max_cpu_time; + max_cpu_time.rlim_cur = max_cpu_time.rlim_max = (rlim_t) ((config->max_cpu_time + 1000) / 1000); + if (setrlimit(RLIMIT_CPU, &max_cpu_time) != 0) { + CHILD_ERROR_EXIT(SETRLIMIT_FAILED); + } + } + + // set max process number limit + if (config->max_process_number != UNLIMITED) { + struct rlimit max_process_number; + max_process_number.rlim_cur = max_process_number.rlim_max = (rlim_t) config->max_process_number; + if (setrlimit(RLIMIT_NPROC, &max_process_number) != 0) { + CHILD_ERROR_EXIT(SETRLIMIT_FAILED); + } + } + + // set max output size limit + if (config->max_output_size != UNLIMITED) { + struct rlimit max_output_size; + max_output_size.rlim_cur = max_output_size.rlim_max = (rlim_t ) config->max_output_size; + if (setrlimit(RLIMIT_FSIZE, &max_output_size) != 0) { + CHILD_ERROR_EXIT(SETRLIMIT_FAILED); + } + } + + FILE *input_file = NULL, *output_file = NULL, *error_file = NULL; + if (config->input_path != NULL) { + input_file = fopen(config->input_path, "r"); + if (input_file == NULL) { + CHILD_ERROR_EXIT(DUP2_FAILED); + } + // redirect file -> stdin + // On success, these system calls return the new descriptor. + // On error, -1 is returned, and errno is set appropriately. + if (dup2(fileno(input_file), fileno(stdin)) == -1) { + // todo log + close_file(input_file); + CHILD_ERROR_EXIT(DUP2_FAILED); + } + } + + if (config->output_path != NULL) { + output_file = fopen(config->output_path, "w"); + if (output_file == NULL) { + close_file(input_file); + CHILD_ERROR_EXIT(DUP2_FAILED); + } + // redirect stdout -> file + if (dup2(fileno(output_file), fileno(stdout)) == -1) { + close_file(input_file, output_file); + CHILD_ERROR_EXIT(DUP2_FAILED); + } + } + + if (config->error_path != NULL) { + // if outfile and error_file is the same path, we use the same file pointer + if (strcmp(config->output_path, config->error_path) == 0) { + error_file = output_file; + } + else { + error_file = fopen(config->error_path, "w"); + if (error_file == NULL) { + // todo log + close_file(input_file, output_file); + CHILD_ERROR_EXIT(DUP2_FAILED); + } + } + // redirect stderr -> file + if (dup2(fileno(error_file), fileno(stderr)) == -1) { + // todo log + close_file(input_file, output_file, error_file); + CHILD_ERROR_EXIT(DUP2_FAILED); + } + } + + // set gid + gid_t group_list[] = {config->gid}; + if (config->gid != -1 && (setgid(config->gid) == -1 || setgroups(sizeof(group_list) / sizeof(gid_t), group_list) == -1)) { + CHILD_ERROR_EXIT(SETUID_FAILED); + } + + // set uid + if (config->uid != -1 && setuid(config->uid) == -1) { + CHILD_ERROR_EXIT(SETUID_FAILED); + } + + // load seccomp so + if (config->seccomp_rule_so_path != NULL) { + void *handler = dlopen(config->seccomp_rule_so_path, RTLD_LAZY); + int (*load_seccomp)(void *, runner_config *); + + if (!handler) { + LOG_FATAL(log_fp, "seccomp failed"); + CHILD_ERROR_EXIT(LOAD_SECCOMP_FAILED); + } + load_seccomp = dlsym(handler, "load_seccomp"); + if (load_seccomp(handler, config) != 0) { + CHILD_ERROR_EXIT(LOAD_SECCOMP_FAILED); + } + } + + execve(config->exe_path, config->args, config->env); + CHILD_ERROR_EXIT(EXECVE_FAILED); + return 0; +} diff --git a/src/child.h b/src/child.h new file mode 100644 index 0000000..c45f7c4 --- /dev/null +++ b/src/child.h @@ -0,0 +1,16 @@ +#ifndef JUDGER_CHILD_H +#define JUDGER_CHILD_H + +#include "runner.h" + +#define CHILD_ERROR_EXIT(error_code)\ + {\ + LOG_ERROR(error_code); \ + raise(SIGUSR1); \ + return -1; \ + } + + +int child_process(void *config); + +#endif //JUDGER_CHILD_H diff --git a/src/killer.c b/src/killer.c new file mode 100644 index 0000000..9cdcea2 --- /dev/null +++ b/src/killer.c @@ -0,0 +1,33 @@ +#define _POSIX_SOURCE +#include +#include +#include + +#include "killer.h" + + +int kill_pid(pid_t pid) { + return kill(pid, SIGKILL); +} + + +void *timeout_killer(void *timeout_killer_args) { + // this is a new thread, kill the process if timeout + pid_t pid = ((struct timeout_killer_args *)timeout_killer_args)->pid; + int timeout = ((struct timeout_killer_args *)timeout_killer_args)->timeout; + // On success, pthread_detach() returns 0; on error, it returns an error number. + if (pthread_detach(pthread_self()) != 0) { + kill_pid(pid); + return NULL; + } + // usleep can't be used, for time args must < 1000ms + // this may sleep longer that expected, but we will have a check at the end + if (sleep((unsigned int)((timeout + 1000) / 1000)) != 0) { + kill_pid(pid); + return NULL; + } + if (kill_pid(pid) != 0) { + return NULL; + } + return NULL; +} \ No newline at end of file diff --git a/src/killer.h b/src/killer.h new file mode 100644 index 0000000..22c3dae --- /dev/null +++ b/src/killer.h @@ -0,0 +1,13 @@ +#ifndef JUDGER_KILLER_H +#define JUDGER_KILLER_H + +struct timeout_killer_args { + int pid; + int timeout; +}; + +int kill_pid(pid_t pid); + +void *timeout_killer(void *timeout_killer_args); + +#endif //JUDGER_KILLER_H diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 0000000..6fc2910 --- /dev/null +++ b/src/logger.c @@ -0,0 +1,66 @@ +#define _POSIX_SOURCE +#include +#include +#include +#include +#include +#include + +#include "logger.h" + +#define log_buffer_size 8192 + + +FILE *log_open(const char *filename) { + FILE *log_fp = fopen(filename, "a"); + if (log_fp == NULL) { + fprintf(stderr, "can not open log file %s", filename); + } + return log_fp; +} + + +void log_close(FILE *log_fp) { + if (log_fp != NULL) { + fclose(log_fp); + } +} + + +void log_write(int level, const char *source_filename, const int line, const FILE *log_fp, const char *fmt, ...) { + char LOG_LEVEL_NOTE[][10] = {"FATAL", "WARNING", "INFO", "DEBUG"}; + if (log_fp == NULL) { + fprintf(stderr, "can not open log file"); + return; + } + static char buffer[log_buffer_size]; + static char log_buffer[log_buffer_size]; + static char datetime[100]; + static char line_str[20]; + static time_t now; + now = time(NULL); + + strftime(datetime, 99, "%Y-%m-%d %H:%M:%S", localtime(&now)); + snprintf(line_str, 19, "%d", line); + va_list ap; + va_start(ap, fmt); + vsnprintf(log_buffer, log_buffer_size, fmt, ap); + va_end(ap); + + int count = snprintf(buffer, log_buffer_size, + "%s [%s] [%s:%d]%s\n", + LOG_LEVEL_NOTE[level], datetime, source_filename, line, log_buffer); + fprintf(stdout, "%s", buffer); + int log_fd = fileno((FILE *) log_fp); + if (flock(log_fd, LOCK_EX) == 0) { + if (write(log_fd, buffer, (size_t) count) < 0) { + fprintf(stderr, "write error"); + return; + } + flock(log_fd, LOCK_UN); + } + else { + fprintf(stderr, "flock error"); + return; + } +} \ No newline at end of file diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..31640b4 --- /dev/null +++ b/src/logger.h @@ -0,0 +1,21 @@ +#ifndef JUDGER_LOGGER_H +#define JUDGER_LOGGER_H + +#define LOG_LEVEL_FATAL 0 +#define LOG_LEVEL_WARNING 1 +#define LOG_LEVEL_INFO 2 +#define LOG_LEVEL_DEBUG 3 + + +FILE *log_open(const char *); + +void log_close(FILE *); + +void log_write(int level, const char *source_filename, const int line_number, const FILE *log_fp, const char *, ...); + +#define LOG_DEBUG(log_fp, x...) log_write(LOG_LEVEL_DEBUG, __FILE__, __LINE__, log_fp, ##x) +#define LOG_INFO(log_fp, x...) log_write(LOG_LEVEL_INFO, __FILE__ __LINE__, log_fp, ##x) +#define LOG_WARNING(log_fp, x...) log_write(LOG_LEVEL_WARNING, __FILE__, __LINE__, log_fp, ##x) +#define LOG_FATAL(log_fp, x...) log_write(LOG_LEVEL_FATAL, __FILE__, __LINE__, log_fp, ##x) + +#endif //JUDGER_LOGGER_H diff --git a/src/rules/Python/rule.c b/src/rules/Python/rule.c new file mode 100644 index 0000000..9362b2a --- /dev/null +++ b/src/rules/Python/rule.c @@ -0,0 +1,8 @@ +#include + +#include "../../runner.h" + + +int load_seccomp(void *dl_handler) { + return 0; +} diff --git a/src/rules/c_cpp/rule.c b/src/rules/c_cpp/rule.c new file mode 100644 index 0000000..bb1cbf3 --- /dev/null +++ b/src/rules/c_cpp/rule.c @@ -0,0 +1,39 @@ +#include +#include + +#include "../../runner.h" + + +int load_seccomp(void *dl_handler, runner_config *config) { + int syscalls_whitelist[] = {SCMP_SYS(read), SCMP_SYS(fstat), + SCMP_SYS(mmap), SCMP_SYS(mprotect), + SCMP_SYS(munmap), SCMP_SYS(open), + SCMP_SYS(arch_prctl), SCMP_SYS(brk), + SCMP_SYS(access), SCMP_SYS(exit_group), + SCMP_SYS(close)}; + int syscalls_whitelist_length = sizeof(syscalls_whitelist) / sizeof(int); + scmp_filter_ctx ctx = NULL; + // load seccomp rules + ctx = seccomp_init(SCMP_ACT_KILL); + if (!ctx) { + return LOAD_SECCOMP_FAILED; + } + for (int i = 0; i < syscalls_whitelist_length; i++) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls_whitelist[i], 0) != 0) { + return 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->exe_path)) != 0) { + return 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) { + return LOAD_SECCOMP_FAILED; + } + if (seccomp_load(ctx) != 0) { + return LOAD_SECCOMP_FAILED; + } + seccomp_release(ctx); + return 0; +} \ No newline at end of file diff --git a/src/runner.c b/src/runner.c new file mode 100644 index 0000000..c47837b --- /dev/null +++ b/src/runner.c @@ -0,0 +1,126 @@ +#define _GNU_SOURCE +#define _POSIX_SOURCE + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "runner.h" +#include "killer.h" +#include "child.h" +#include "logger.h" + +#define STACK_SIZE (2 * 1024 * 1024) + +void init_result(runner_result *result) { + result->error = SUCCESS; + result->cpu_time = result->real_time = result->signal = result->exit_code = 0; + result->memory = 0; +} + + +void run(runner_config *config, runner_result *result) { + // init log fp + FILE *log_fp = log_open(config->log_path); + + // init result + init_result(result); + + // check whether current user is root + uid_t uid = getuid(); + if (uid != 0) { + ERROR_EXIT(ROOT_REQUIRED); + } + + // check args + if ((config->max_cpu_time < 1 && config->max_cpu_time != UNLIMITED) || + (config->max_real_time < 1 && config->max_real_time != UNLIMITED) || + (config->max_memory < 1 && config->max_memory != UNLIMITED) || + (config->max_process_number < 1 && config->max_process_number != UNLIMITED) || + (config->max_output_size < 1 && config->max_output_size != UNLIMITED)) { + ERROR_EXIT(INVALID_CONFIG); + } + + // malloc stack for child process + char *stack = NULL; + stack = malloc(STACK_SIZE); + if (stack == NULL) { + ERROR_EXIT(CLONE_FAILED); + } + + // record current time + struct timeval start, end; + gettimeofday(&start, NULL); + + // clone + child_args args; + args.config = config; + args.log_fp = log_fp; + + pid_t child_pid = clone(child_process, stack + STACK_SIZE, SIGCHLD, (void *) (&args)); + + // pid < 0 shows clone failed + if (child_pid < 0) { + ERROR_EXIT(CLONE_FAILED); + } + else { + // create new thread to monitor process running time + pthread_t tid = 0; + if (config->max_real_time != UNLIMITED) { + struct timeout_killer_args killer_args; + + killer_args.timeout = config->max_real_time; + killer_args.pid = child_pid; + if (pthread_create(&tid, NULL, timeout_killer, (void *) (&killer_args)) != 0) { + kill_pid(child_pid); + ERROR_EXIT(PTHREAD_FAILED); + } + } + + int status; + struct rusage resource_usage; + + // wait for child process to terminate + // on success, returns the process ID of the child whose state has changed; + // On error, -1 is returned. + if (wait4(child_pid, &status, 0, &resource_usage) == -1) { + kill_pid(child_pid); + ERROR_EXIT(WAIT_FAILED); + } + + // process exited, we may need to cancel timeout killer thread + if (config->max_real_time != UNLIMITED) { + if (pthread_cancel(tid) != 0) { + // todo logging + }; + } + + result->exit_code = WEXITSTATUS(status); + result->cpu_time = (int) (resource_usage.ru_utime.tv_sec * 1000 + + resource_usage.ru_utime.tv_usec / 1000 + + resource_usage.ru_stime.tv_sec * 1000 + + resource_usage.ru_stime.tv_usec / 1000); + result->memory = resource_usage.ru_maxrss * 1024; + + // if signaled + if (WIFSIGNALED(status) != 0) { + LOG_DEBUG(log_fp, "signal: %d", WTERMSIG(status)); + result->signal = WTERMSIG(status); + } + + // get end time + 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); + } +} diff --git a/src/runner.h b/src/runner.h new file mode 100644 index 0000000..7ea368c --- /dev/null +++ b/src/runner.h @@ -0,0 +1,80 @@ +#ifndef JUDGER_RUNNER_H +#define JUDGER_RUNNER_H + +#include +#include + +#define UNLIMITED -1 +#define DO_NOT_CHANGE -1 + + +#define LOG_ERROR(error_code) LOG_FATAL(log_fp, "Error: "#error_code); + +#define ERROR_EXIT(error_code)\ + {\ + LOG_ERROR(error_code); \ + result->error = error_code; \ + log_close(log_fp); \ + return; \ + } + +enum { + SUCCESS = 0, + INVALID_CONFIG = -1, + CLONE_FAILED = -2, + PTHREAD_FAILED = -3, + WAIT_FAILED = -4, + ROOT_REQUIRED = -5, + LOAD_SECCOMP_FAILED = -6, + SETRLIMIT_FAILED = -7, + DUP2_FAILED = -8, + SETUID_FAILED = -9, + EXECVE_FAILED = -10 +}; + + +enum { + CPU_TIME_LIMITED = 1, + REAL_TIME_LIMIT_EXCEEDED = 2, + MEMORY_LIMIT_EXCEEDED = 3, + RUNTIME_ERROR = 4, + SYSTEM_ERROR = 5 +}; + + +typedef struct config { + int max_cpu_time; + int max_real_time; + long max_memory; + int max_process_number; + long max_output_size; + char *exe_path; + char *input_path; + char *output_path; + char *error_path; + char *args[256]; + char *env[256]; + char *log_path; + char *seccomp_rule_so_path; + uid_t uid; + gid_t gid; +} runner_config; + + +typedef struct result { + int cpu_time; + int real_time; + long memory; + int signal; + int exit_code; + int error; +} runner_result; + + +typedef struct child_args { + FILE *log_fp; + runner_config *config; +} child_args; + +void run(runner_config *config, runner_result *result); +#endif //JUDGER_RUNNER_H diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..c57c4c0 --- /dev/null +++ b/test/main.c @@ -0,0 +1,30 @@ +#include +#include "../src/runner.h" + +int main() { + runner_config config; + config.max_cpu_time = 100; + config.max_real_time = 100; + config.max_memory = 1024 * 1024 * 1024; + config.max_process_number = 1024; + config.max_output_size = 1024 * 1024 * 64; + config.log_path = "log.log"; + config.seccomp_rule_so_path = NULL;//"/home/virusdefender/ClionProjects/Judger/output/lib/librule_c_cpp.so"; + config.input_path = "/dev/null"; + config.output_path = "output.txt"; + config.error_path = "error.txt"; + config.exe_path = "/bin/ls"; + + config.args[0] = "/bin/ls"; + config.args[1] = "/dev"; + config.args[2] = NULL; + + config.env[0] = "PATH=test"; + config.env[1] = NULL; + + runner_result result; + run(&config, &result); + + printf("cpu time:%d\nreal time:%d\nmemory:%ld\nsignal:%d\nexit code:%d\nerror:%d", result.cpu_time, result.real_time, result.memory, result.signal, result.exit_code, result.error); + return 0; +} diff --git a/test/test_helper.h b/test/test_helper.h new file mode 100644 index 0000000..6ae0539 --- /dev/null +++ b/test/test_helper.h @@ -0,0 +1,57 @@ +/* This is a really minimal testing framework for C. + * + * Example: + * + * test_cond("Check if 1 == 1", 1==1) + * test_cond("Check if 5 > 10", 5 > 10) + * test_report() + * + * ---------------------------------------------------------------------------- + * + * Copyright (c) 2010-2012, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JDGER_TESTHELP_H +#define JDGER_TESTHELP_H + +int __failed_tests = 0; +int __test_num = 0; +#define test_cond(descr,_c) do { \ + __test_num++; printf("%d - %s: ", __test_num, descr); \ + if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \ +} while(0); +#define test_report() do { \ + printf("%d tests, %d passed, %d failed\n", __test_num, \ + __test_num-__failed_tests, __failed_tests); \ + if (__failed_tests) { \ + printf("=== WARNING === We have failed tests here...\n"); \ + exit(1); \ + } \ +} while(0); + +#endif \ No newline at end of file diff --git a/tests/1/Main.c b/tests/1/Main.c deleted file mode 100644 index 9d9d7d5..0000000 --- a/tests/1/Main.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 正常运行的程序 - */ - -#include -#include - -int main() -{ - char input[100]; - scanf("%s", input); - printf("%s\n", input); - printf("Hello world"); - return 0; -} \ No newline at end of file diff --git a/tests/1/config b/tests/1/config deleted file mode 100644 index 4412869..0000000 --- a/tests/1/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/1/in b/tests/1/in deleted file mode 100644 index 6a537b5..0000000 --- a/tests/1/in +++ /dev/null @@ -1 +0,0 @@ -1234567890 \ No newline at end of file diff --git a/tests/1/out b/tests/1/out deleted file mode 100644 index a9299e1..0000000 --- a/tests/1/out +++ /dev/null @@ -1,2 +0,0 @@ -1234567890 -Hello world \ No newline at end of file diff --git a/tests/1/result b/tests/1/result deleted file mode 100644 index ebd96f5..0000000 --- a/tests/1/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0} \ No newline at end of file diff --git a/tests/10/Main.c b/tests/10/Main.c deleted file mode 100644 index 6719118..0000000 --- a/tests/10/Main.c +++ /dev/null @@ -1,11 +0,0 @@ -/* - * 测试cpu时间超限 - */ -#include -#include - - -int main(int argc, char *argv[]) { - while(1); - return 0; -} diff --git a/tests/10/config b/tests/10/config deleted file mode 100644 index 73125fb..0000000 --- a/tests/10/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 300, "max_memory": 200000000} diff --git a/tests/10/in b/tests/10/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/10/out b/tests/10/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/10/result b/tests/10/result deleted file mode 100644 index a3a0ccc..0000000 --- a/tests/10/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 1, "signal": 26} diff --git a/tests/11/Main.c b/tests/11/Main.c deleted file mode 100644 index 7b40b65..0000000 --- a/tests/11/Main.c +++ /dev/null @@ -1,11 +0,0 @@ -/* - * 测试实际运行时间超限 - */ -#include -#include - - -int main(int argc, char *argv[]) { - sleep(1000); - return 0; -} diff --git a/tests/11/config b/tests/11/config deleted file mode 100644 index 9e8f3c1..0000000 --- a/tests/11/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 300, "max_memory": 200000000, "use_sandbox": false} diff --git a/tests/11/in b/tests/11/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/11/out b/tests/11/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/11/result b/tests/11/result deleted file mode 100644 index cd9f00b..0000000 --- a/tests/11/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 2, "signal": 14} diff --git a/tests/12/Main.c b/tests/12/Main.c deleted file mode 100644 index 54477da..0000000 --- a/tests/12/Main.c +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 测试内存限制,允许分配的内存为限制参数2倍,本测试用例分配了150M,在两倍范围内,能分配成功,最后被标记为内存超限 - */ -#include -#include -#include - -int main() -{ - // 150m - int big_size = 150 * 1024 * 1024; - - int *b = NULL; - - b = (int *)malloc(big_size); - memset(b, 0, big_size); - return 0; -} diff --git a/tests/12/config b/tests/12/config deleted file mode 100644 index 474ea7b..0000000 --- a/tests/12/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 300, "max_memory": 100000000} diff --git a/tests/12/in b/tests/12/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/12/out b/tests/12/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/12/result b/tests/12/result deleted file mode 100644 index 2ce151d..0000000 --- a/tests/12/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 3, "signal": 0} diff --git a/tests/13/Main.c b/tests/13/Main.c deleted file mode 100644 index 473d6e0..0000000 --- a/tests/13/Main.c +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 测试内存限制,允许分配的内存为限制参数2倍,本测试用例分配了300M,超过两倍范围内,不能分配成功,运行出错 - */ -#include -#include -#include - -int main() -{ - // 300m - int big_size = 300 * 1024 * 1024; - - int *b = NULL; - - b = (int *)malloc(big_size); - memset(b, 0, big_size); - return 0; -} diff --git a/tests/13/config b/tests/13/config deleted file mode 100644 index b022399..0000000 --- a/tests/13/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 3000, "max_memory": 50000000} diff --git a/tests/13/in b/tests/13/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/13/out b/tests/13/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/13/result b/tests/13/result deleted file mode 100644 index 3a05427..0000000 --- a/tests/13/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 4, "signal": 11} diff --git a/tests/14/Main.c b/tests/14/Main.c deleted file mode 100644 index b9599fe..0000000 --- a/tests/14/Main.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 测试stdout和stderr重定向 - */ - -#include -#include - -int main() -{ - char input[100]; - scanf("%s", input); - fprintf(stdout, "%s\n", input); - fprintf(stderr, "%s\n", input); - return 0; -} diff --git a/tests/14/config b/tests/14/config deleted file mode 100644 index 4412869..0000000 --- a/tests/14/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/14/in b/tests/14/in deleted file mode 100644 index 6a537b5..0000000 --- a/tests/14/in +++ /dev/null @@ -1 +0,0 @@ -1234567890 \ No newline at end of file diff --git a/tests/14/out b/tests/14/out deleted file mode 100644 index 8d1e93a..0000000 --- a/tests/14/out +++ /dev/null @@ -1,2 +0,0 @@ -1234567890 -1234567890 diff --git a/tests/14/result b/tests/14/result deleted file mode 100644 index ebd96f5..0000000 --- a/tests/14/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0} \ No newline at end of file diff --git a/tests/15/Main.c b/tests/15/Main.c deleted file mode 100644 index 587a0cd..0000000 --- a/tests/15/Main.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 测试返回值 - */ - -#include -#include - -int main() -{ - char input[100]; - scanf("%s", input); - fprintf(stdout, "%s\n", input); - fprintf(stderr, "%s\n", input); - return 17; -} diff --git a/tests/15/config b/tests/15/config deleted file mode 100644 index 4412869..0000000 --- a/tests/15/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/15/in b/tests/15/in deleted file mode 100644 index 6a537b5..0000000 --- a/tests/15/in +++ /dev/null @@ -1 +0,0 @@ -1234567890 \ No newline at end of file diff --git a/tests/15/out b/tests/15/out deleted file mode 100644 index 8d1e93a..0000000 --- a/tests/15/out +++ /dev/null @@ -1,2 +0,0 @@ -1234567890 -1234567890 diff --git a/tests/15/result b/tests/15/result deleted file mode 100644 index 17d5171..0000000 --- a/tests/15/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 4, "signal": 0, "exit_status": 17} diff --git a/tests/16/Main.c b/tests/16/Main.c deleted file mode 100644 index 37c3f2d..0000000 --- a/tests/16/Main.c +++ /dev/null @@ -1,12 +0,0 @@ -/* - * uid gid test - */ - -#include -#include - -int main() -{ - printf("uid %d gid %d\n", getuid(), getgid()); - return 0; -} diff --git a/tests/16/config b/tests/16/config deleted file mode 100644 index ba5ab61..0000000 --- a/tests/16/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000, "use_sandbox": false, "use_nobody": true} diff --git a/tests/16/in b/tests/16/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/16/out b/tests/16/out deleted file mode 100644 index e6acde5..0000000 --- a/tests/16/out +++ /dev/null @@ -1 +0,0 @@ -uid 65534 gid 65534 diff --git a/tests/16/result b/tests/16/result deleted file mode 100644 index d0b1583..0000000 --- a/tests/16/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0, "exit_status": 0} diff --git a/tests/17/Main.c b/tests/17/Main.c deleted file mode 100644 index e20c070..0000000 --- a/tests/17/Main.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include -#include -#include -#include - -int main() -{ - int status; - int pid; - - if ((pid = fork()) < 0) { - perror("fork error"); - return 0; - } - - if (pid == 0) { - while (1) {}; - } - else { - struct rusage resource_usage; - if (wait4(pid, &status, 0, &resource_usage) == -1) { - perror("wait4 error!"); - } - } - - return 0; -} diff --git a/tests/17/config b/tests/17/config deleted file mode 100644 index cad9f30..0000000 --- a/tests/17/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000, "use_sandbox": false} diff --git a/tests/17/in b/tests/17/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/17/out b/tests/17/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/17/result b/tests/17/result deleted file mode 100644 index b0f11da..0000000 --- a/tests/17/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0} diff --git a/tests/18/Main.c b/tests/18/Main.c deleted file mode 100644 index 31a9002..0000000 --- a/tests/18/Main.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -int main() -{ - system("id"); - return 0; -} diff --git a/tests/18/config b/tests/18/config deleted file mode 100644 index 118f197..0000000 --- a/tests/18/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000, "use_sandbox": false, "use_nobody": true} diff --git a/tests/18/in b/tests/18/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/18/out b/tests/18/out deleted file mode 100644 index 850c947..0000000 --- a/tests/18/out +++ /dev/null @@ -1 +0,0 @@ -uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup) diff --git a/tests/18/result b/tests/18/result deleted file mode 100644 index b0f11da..0000000 --- a/tests/18/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0} diff --git a/tests/2/Main.c b/tests/2/Main.c deleted file mode 100644 index f1a524d..0000000 --- a/tests/2/Main.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 正常运行的程序 - */ - -#include -#include -#include - -int main() -{ - // 1k - int small_size = 1024; - // 150m - int big_size = 150 * 1024 * 1024; - - int *s = NULL, *b = NULL; - - s = (int *)malloc(small_size); - if(s){ - printf("malloc small size succedeed\n"); - } - else{ - printf("malloc small size failed\n"); - return -1; - } - - b = (int *)malloc(big_size); - if(b){ - printf("malloc big size succedeed\n"); - } - else{ - printf("malloc big size failed\n"); - return -2; - } - return 0; -} \ No newline at end of file diff --git a/tests/2/config b/tests/2/config deleted file mode 100644 index 41e3a53..0000000 --- a/tests/2/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000} \ No newline at end of file diff --git a/tests/2/in b/tests/2/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/2/out b/tests/2/out deleted file mode 100644 index 9929a09..0000000 --- a/tests/2/out +++ /dev/null @@ -1,2 +0,0 @@ -malloc small size succedeed -malloc big size succedeed diff --git a/tests/2/result b/tests/2/result deleted file mode 100644 index ebd96f5..0000000 --- a/tests/2/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0} \ No newline at end of file diff --git a/tests/3/Main.c b/tests/3/Main.c deleted file mode 100644 index 0c1d95b..0000000 --- a/tests/3/Main.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 不允许的系统调用 clone - */ - -#include -#include -#include - -int main() -{ - pid_t pid; - printf("fork test\n"); - pid = fork(); - if(pid < 0){ - printf("fork failed\n"); - } - else { - printf("fork succeeded\n"); - } - return 0; -} \ No newline at end of file diff --git a/tests/3/config b/tests/3/config deleted file mode 100644 index 4412869..0000000 --- a/tests/3/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/3/in b/tests/3/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/3/out b/tests/3/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/3/result b/tests/3/result deleted file mode 100644 index 44a7510..0000000 --- a/tests/3/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 4, "signal": 31} \ No newline at end of file diff --git a/tests/4/Main.c b/tests/4/Main.c deleted file mode 100644 index 7270bb5..0000000 --- a/tests/4/Main.c +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 不允许的系统调用 exec - */ - -#include -#include -#include - -int main(int arg, char **args) { - char *argv[] = {"/bin/echo", "1234567890", NULL}; - char *envp[] = {NULL}; - printf("execve test\n"); - execve("/bin/echo", argv, envp); -} diff --git a/tests/4/config b/tests/4/config deleted file mode 100644 index 4412869..0000000 --- a/tests/4/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/4/in b/tests/4/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/4/out b/tests/4/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/4/result b/tests/4/result deleted file mode 100644 index 44a7510..0000000 --- a/tests/4/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 4, "signal": 31} \ No newline at end of file diff --git a/tests/5/Main.c b/tests/5/Main.c deleted file mode 100644 index 58180fe..0000000 --- a/tests/5/Main.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 自定义构造函数执行fork - */ - -#include -#include -#include -#include -#include -#include - -__attribute((constructor)) -void myinit(void) -{ - if(fork() < 0) { - printf("fork failed\n"); - } - else { - printf("fork succeeded\n"); - } -} - -int main(void) -{ - return 0; -} \ No newline at end of file diff --git a/tests/5/config b/tests/5/config deleted file mode 100644 index 4412869..0000000 --- a/tests/5/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/5/in b/tests/5/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/5/out b/tests/5/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/5/result b/tests/5/result deleted file mode 100644 index 44a7510..0000000 --- a/tests/5/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 4, "signal": 31} \ No newline at end of file diff --git a/tests/6/Main.c b/tests/6/Main.c deleted file mode 100644 index dace805..0000000 --- a/tests/6/Main.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 不允许的系统调用 write file - */ - -#include -#include - -int main() -{ - FILE * fp; - - fp = fopen ("/dev/null", "w"); - fprintf(fp, "test"); - fclose(fp); - printf("write file succeesed"); - return 0; -} \ No newline at end of file diff --git a/tests/6/config b/tests/6/config deleted file mode 100644 index 4412869..0000000 --- a/tests/6/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/6/in b/tests/6/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/6/out b/tests/6/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/6/result b/tests/6/result deleted file mode 100644 index 44a7510..0000000 --- a/tests/6/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 4, "signal": 31} \ No newline at end of file diff --git a/tests/7/Main.c b/tests/7/Main.c deleted file mode 100644 index 2d2c524..0000000 --- a/tests/7/Main.c +++ /dev/null @@ -1,13 +0,0 @@ -/* - * 执行系统命令 - */ - -#include -#include -#include - -int main() -{ - system("/bin/echo test"); - return 0; -} \ No newline at end of file diff --git a/tests/7/config b/tests/7/config deleted file mode 100644 index 4412869..0000000 --- a/tests/7/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000} \ No newline at end of file diff --git a/tests/7/in b/tests/7/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/7/out b/tests/7/out deleted file mode 100644 index e69de29..0000000 diff --git a/tests/7/result b/tests/7/result deleted file mode 100644 index 44a7510..0000000 --- a/tests/7/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 4, "signal": 31} \ No newline at end of file diff --git a/tests/8/Main.c b/tests/8/Main.c deleted file mode 100644 index 15bded4..0000000 --- a/tests/8/Main.c +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 测试args参数 - */ -#include - - -int main(int argc, char *argv[]) { - int j; - for (j = 0; j < argc; j++) - printf("argv[%d]: %s\n", j, argv[j]); - return 0; -} diff --git a/tests/8/config b/tests/8/config deleted file mode 100644 index 5c95759..0000000 --- a/tests/8/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000, "args": ["hello", "123"]} \ No newline at end of file diff --git a/tests/8/in b/tests/8/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/8/out b/tests/8/out deleted file mode 100644 index 25e217d..0000000 --- a/tests/8/out +++ /dev/null @@ -1,3 +0,0 @@ -argv[0]: /tmp/judger_test/8 -argv[1]: hello -argv[2]: 123 diff --git a/tests/8/result b/tests/8/result deleted file mode 100644 index ebd96f5..0000000 --- a/tests/8/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0} \ No newline at end of file diff --git a/tests/9/Main.c b/tests/9/Main.c deleted file mode 100644 index 9ae5872..0000000 --- a/tests/9/Main.c +++ /dev/null @@ -1,11 +0,0 @@ -/* - * 测试环境变量参数 - */ -#include -#include - - -int main(int argc, char *argv[]) { - printf("%s\n%s\n", getenv("hello"), getenv("123")); - return 0; -} diff --git a/tests/9/config b/tests/9/config deleted file mode 100644 index f259039..0000000 --- a/tests/9/config +++ /dev/null @@ -1 +0,0 @@ -{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000, "env": ["hello=world", "123=456"]} diff --git a/tests/9/in b/tests/9/in deleted file mode 100644 index e69de29..0000000 diff --git a/tests/9/out b/tests/9/out deleted file mode 100644 index a008214..0000000 --- a/tests/9/out +++ /dev/null @@ -1,2 +0,0 @@ -world -456 diff --git a/tests/9/result b/tests/9/result deleted file mode 100644 index ebd96f5..0000000 --- a/tests/9/result +++ /dev/null @@ -1 +0,0 @@ -{"flag": 0, "signal": 0} \ No newline at end of file diff --git a/tests/test.py b/tests/test.py deleted file mode 100644 index 129b6a1..0000000 --- a/tests/test.py +++ /dev/null @@ -1,151 +0,0 @@ -# coding=utf-8 -import os -import pwd -import json -import judger -import shutil -from unittest import TestCase, main - - -class JudgerTest(TestCase): - def setUp(self): - self.tmp_path = "/tmp/judger_test" - - def compile_src(self, src_path, language, exe_path): - if language == "c": - return os.system("gcc %s -o %s" % (src_path, exe_path)) - elif language == "cpp": - return os.system("g++ %s -o %s" % (src_path, exe_path)) - elif language == "java": - pass - else: - raise ValueError("Invalid language") - - def test_run(self): - shutil.rmtree(self.tmp_path, ignore_errors=True) - os.mkdir(self.tmp_path) - for i in os.listdir("."): - try: - int(i) - except Exception: - continue - print "\n\nRunning test: ", i - test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), str(i)) - exe_path = os.path.join(self.tmp_path, str(i)) - config = json.loads(open(os.path.join(test_dir, "config")).read()) - self.assertEqual(self.compile_src(os.path.join(test_dir, "Main.c"), config.pop("language"), exe_path), 0) - - run_result = judger.run(path=exe_path, - in_file=os.path.join(test_dir, "in"), - out_file=os.path.join(self.tmp_path, str(i) + ".out"), - **config) - result = json.loads(open(os.path.join(test_dir, "result")).read()) - print run_result - self.assertEqual(result["flag"], run_result["flag"]) - self.assertEqual(result["signal"], run_result["signal"]) - self.assertEqual(open(os.path.join(test_dir, "out")).read(), - open(os.path.join(self.tmp_path, str(i) + ".out")).read()) - self._judger_cpu_time_args_check() - self._judger_memory_args_check() - self._judger_exec_file_args_check() - self._judger_in_file_args_check() - self._judger_args_args_check() - self._judger_env_args_check() - self._judger_child_process_cpu_time_check() - self._judger_user_args_check() - - def _judger_cpu_time_args_check(self): - with self.assertRaisesRegexp(ValueError, "max_cpu_time must > 1 ms or unlimited"): - judger.run(path="/bin/true", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=0, max_memory=200000000, - env=["aaa=123"], use_sandbox=False, use_nobody=False) - try: - judger.run(path="/bin/true", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=judger.CPU_TIME_UNLIMITED, max_memory=200000000, - env=["aaa=123"], use_sandbox=False, use_nobody=False) - except Exception as e: - self.fail(e.message) - - def _judger_memory_args_check(self): - with self.assertRaisesRegexp(ValueError, "max_memory must > 16M or unlimited"): - judger.run(path="/bin/true", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=100, - env=["aaa=123"], use_sandbox=True, use_nobody=True) - try: - judger.run(path="/bin/true", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=judger.MEMORY_UNLIMITED, - env=["aaa=123"], use_sandbox=True, use_nobody=True) - except Exception as e: - self.fail(e.message) - - - def _judger_exec_file_args_check(self): - with self.assertRaisesRegexp(ValueError, "Exec file does not exist"): - judger.run(path="/bin/not_found", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - env=["aaa=123"], use_sandbox=True, use_nobody=True) - - def _judger_in_file_args_check(self): - with self.assertRaisesRegexp(ValueError, "in_file does not exist"): - judger.run(path="/bin/true", in_file="/dev/xxx", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - env=["aaa=123"], use_sandbox=True, use_nobody=True) - - def _judger_args_args_check(self): - with self.assertRaisesRegexp(ValueError, "args must be a list"): - judger.run(path="/bin/ls", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - args=123, use_sandbox=True, use_nobody=True) - - with self.assertRaisesRegexp(ValueError, "arg in args must be a string"): - judger.run(path="/bin/ls", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - args=["123", {"a": "b"}], use_sandbox=True, use_nobody=True) - - with self.assertRaisesRegexp(ValueError, "Number of args must < 95"): - judger.run(path="/bin/ls", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - args=["123"] * 200, use_sandbox=True, use_nobody=True) - - def _judger_env_args_check(self): - with self.assertRaisesRegexp(ValueError, "env must be a list"): - judger.run(path="/bin/true", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - env=123, use_sandbox=True, use_nobody=True) - - with self.assertRaisesRegexp(ValueError, "env item must be a string"): - judger.run(path="/bin/ls", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - env=["123", {"a": "b"}], use_sandbox=True, use_nobody=True) - - with self.assertRaisesRegexp(ValueError, "Number of env must < 95"): - judger.run(path="/bin/true", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=1000, max_memory=200000000, - env=["123=345"] * 200, use_sandbox=True, use_nobody=True) - - def _judger_user_args_check(self): - os.setuid(pwd.getpwnam("nobody").pw_uid) - with self.assertRaisesRegexp(ValueError, "Root user is required when use_nobody=True"): - judger.run(path="/bin/ls", in_file="/dev/null", - out_file="/dev/null", max_cpu_time=2000, max_memory=200000000, - env=["aaa=123"], use_sandbox=True, use_nobody=True) - - # child process can not inherit timeout rules from parent process defined by setitimer, so we use setrlimit to - # control child process max running time - def _judger_child_process_cpu_time_check(self): - try: - max_cpu_time = 2000 - result = judger.run(path=os.path.join(self.tmp_path, "17"), in_file="/dev/null", - out_file="/dev/null", max_cpu_time=max_cpu_time, max_memory=200000000, - env=["aaa=123"], use_sandbox=False, use_nobody=True) - expected_cpu_time = (max_cpu_time + 1000) / 1000 * 1000 - if result["cpu_time"] > expected_cpu_time * 1.1: - self.fail("cpu time exceeded") - if result["real_time"] >= max_cpu_time * 3: - self.fail("real time exceeded") - except Exception as e: - self.fail(e.message) - - -if __name__ == "__main__": - main()