mirror of
https://github.com/QingdaoU/Judger.git
synced 2024-12-28 16:01:41 +00:00
new version
This commit is contained in:
parent
93dddbc89e
commit
1a7aadef82
37
LICENSE
37
LICENSE
@ -1,37 +0,0 @@
|
||||
The Star And Thank Author License (SATA)
|
||||
|
||||
Copyright (c) <Qingdao University Online Judge Dev Team> <info@qduoj.com>
|
||||
|
||||
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.
|
34
README.md
34
README.md
@ -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
|
34
demo/demo.c
34
demo/demo.c
@ -1,34 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
49
demo/demo.py
49
demo/demo.py
@ -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()
|
150
judger.c
150
judger.c
@ -1,150 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <python2.7/Python.h>
|
||||
#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);
|
||||
}
|
89
logger.h
89
logger.h
@ -1,89 +0,0 @@
|
||||
#ifndef __LOGGER__
|
||||
#define __LOGGER__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
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
|
290
runner.c
290
runner.c
@ -1,290 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef __APPLE__
|
||||
#include <seccomp.h>
|
||||
#else
|
||||
#warning "###### This judger can not work under OSX, installation is only for dev dependencies! #####"
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <sched.h>
|
||||
#include <grp.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
67
runner.h
67
runner.h
@ -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
|
14
setup.py
14
setup.py
@ -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)])
|
157
src/child.c
Normal file
157
src/child.c
Normal file
@ -0,0 +1,157 @@
|
||||
#define _BSD_SOURCE
|
||||
#define _POSIX_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <grp.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#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;
|
||||
}
|
16
src/child.h
Normal file
16
src/child.h
Normal file
@ -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
|
33
src/killer.c
Normal file
33
src/killer.c
Normal file
@ -0,0 +1,33 @@
|
||||
#define _POSIX_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#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;
|
||||
}
|
13
src/killer.h
Normal file
13
src/killer.h
Normal file
@ -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
|
66
src/logger.c
Normal file
66
src/logger.c
Normal file
@ -0,0 +1,66 @@
|
||||
#define _POSIX_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
21
src/logger.h
Normal file
21
src/logger.h
Normal file
@ -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
|
8
src/rules/Python/rule.c
Normal file
8
src/rules/Python/rule.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <seccomp.h>
|
||||
|
||||
#include "../../runner.h"
|
||||
|
||||
|
||||
int load_seccomp(void *dl_handler) {
|
||||
return 0;
|
||||
}
|
39
src/rules/c_cpp/rule.c
Normal file
39
src/rules/c_cpp/rule.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <seccomp.h>
|
||||
|
||||
#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;
|
||||
}
|
126
src/runner.c
Normal file
126
src/runner.c
Normal file
@ -0,0 +1,126 @@
|
||||
#define _GNU_SOURCE
|
||||
#define _POSIX_SOURCE
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <wait.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
80
src/runner.h
Normal file
80
src/runner.h
Normal file
@ -0,0 +1,80 @@
|
||||
#ifndef JUDGER_RUNNER_H
|
||||
#define JUDGER_RUNNER_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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
|
30
test/main.c
Normal file
30
test/main.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
57
test/test_helper.h
Normal file
57
test/test_helper.h
Normal file
@ -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 <antirez at gmail dot com>
|
||||
* 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
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* 正常运行的程序
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char input[100];
|
||||
scanf("%s", input);
|
||||
printf("%s\n", input);
|
||||
printf("Hello world");
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
@ -1 +0,0 @@
|
||||
1234567890
|
@ -1,2 +0,0 @@
|
||||
1234567890
|
||||
Hello world
|
@ -1 +0,0 @@
|
||||
{"flag": 0, "signal": 0}
|
@ -1,11 +0,0 @@
|
||||
/*
|
||||
* 测试cpu时间超限
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
while(1);
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 300, "max_memory": 200000000}
|
@ -1 +0,0 @@
|
||||
{"flag": 1, "signal": 26}
|
@ -1,11 +0,0 @@
|
||||
/*
|
||||
* 测试实际运行时间超限
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
sleep(1000);
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 300, "max_memory": 200000000, "use_sandbox": false}
|
@ -1 +0,0 @@
|
||||
{"flag": 2, "signal": 14}
|
@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 测试内存限制,允许分配的内存为限制参数2倍,本测试用例分配了150M,在两倍范围内,能分配成功,最后被标记为内存超限
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
// 150m
|
||||
int big_size = 150 * 1024 * 1024;
|
||||
|
||||
int *b = NULL;
|
||||
|
||||
b = (int *)malloc(big_size);
|
||||
memset(b, 0, big_size);
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 300, "max_memory": 100000000}
|
@ -1 +0,0 @@
|
||||
{"flag": 3, "signal": 0}
|
@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 测试内存限制,允许分配的内存为限制参数2倍,本测试用例分配了300M,超过两倍范围内,不能分配成功,运行出错
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
// 300m
|
||||
int big_size = 300 * 1024 * 1024;
|
||||
|
||||
int *b = NULL;
|
||||
|
||||
b = (int *)malloc(big_size);
|
||||
memset(b, 0, big_size);
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 3000, "max_memory": 50000000}
|
@ -1 +0,0 @@
|
||||
{"flag": 4, "signal": 11}
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* 测试stdout和stderr重定向
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char input[100];
|
||||
scanf("%s", input);
|
||||
fprintf(stdout, "%s\n", input);
|
||||
fprintf(stderr, "%s\n", input);
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
@ -1 +0,0 @@
|
||||
1234567890
|
@ -1,2 +0,0 @@
|
||||
1234567890
|
||||
1234567890
|
@ -1 +0,0 @@
|
||||
{"flag": 0, "signal": 0}
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* 测试返回值
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char input[100];
|
||||
scanf("%s", input);
|
||||
fprintf(stdout, "%s\n", input);
|
||||
fprintf(stderr, "%s\n", input);
|
||||
return 17;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
@ -1 +0,0 @@
|
||||
1234567890
|
@ -1,2 +0,0 @@
|
||||
1234567890
|
||||
1234567890
|
@ -1 +0,0 @@
|
||||
{"flag": 4, "signal": 0, "exit_status": 17}
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
* uid gid test
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("uid %d gid %d\n", getuid(), getgid());
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000, "use_sandbox": false, "use_nobody": true}
|
@ -1 +0,0 @@
|
||||
uid 65534 gid 65534
|
@ -1 +0,0 @@
|
||||
{"flag": 0, "signal": 0, "exit_status": 0}
|
@ -1,29 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
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;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000, "use_sandbox": false}
|
@ -1 +0,0 @@
|
||||
{"flag": 0, "signal": 0}
|
@ -1,8 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
system("id");
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000, "use_sandbox": false, "use_nobody": true}
|
@ -1 +0,0 @@
|
||||
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
|
@ -1 +0,0 @@
|
||||
{"flag": 0, "signal": 0}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 正常运行的程序
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 200000000}
|
@ -1,2 +0,0 @@
|
||||
malloc small size succedeed
|
||||
malloc big size succedeed
|
@ -1 +0,0 @@
|
||||
{"flag": 0, "signal": 0}
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* 不允许的系统调用 clone
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
@ -1 +0,0 @@
|
||||
{"flag": 4, "signal": 31}
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
* 不允许的系统调用 exec
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int arg, char **args) {
|
||||
char *argv[] = {"/bin/echo", "1234567890", NULL};
|
||||
char *envp[] = {NULL};
|
||||
printf("execve test\n");
|
||||
execve("/bin/echo", argv, envp);
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
@ -1 +0,0 @@
|
||||
{"flag": 4, "signal": 31}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* 自定义构造函数执行fork
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute((constructor))
|
||||
void myinit(void)
|
||||
{
|
||||
if(fork() < 0) {
|
||||
printf("fork failed\n");
|
||||
}
|
||||
else {
|
||||
printf("fork succeeded\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
@ -1 +0,0 @@
|
||||
{"flag": 4, "signal": 31}
|
@ -1,17 +0,0 @@
|
||||
/*
|
||||
* 不允许的系统调用 write file
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
FILE * fp;
|
||||
|
||||
fp = fopen ("/dev/null", "w");
|
||||
fprintf(fp, "test");
|
||||
fclose(fp);
|
||||
printf("write file succeesed");
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
@ -1 +0,0 @@
|
||||
{"flag": 4, "signal": 31}
|
@ -1,13 +0,0 @@
|
||||
/*
|
||||
* 执行系统命令
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
system("/bin/echo test");
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"language": "c", "max_cpu_time": 2000, "max_memory": 20000000}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user