├── .gitignore ├── Makefile ├── README.md ├── config.ini ├── json.hpp ├── judge.h ├── language.h ├── logger.h ├── meta.json ├── okcall.h ├── okcall_x64.h ├── okcall_x86.h ├── temp └── Main.cpp ├── test.cpp └── testdata ├── a09b1fa7-dd25-4013-a06f-0a04fa857373.in ├── a09b1fa7-dd25-4013-a06f-0a04fa857373.out ├── f5296bf8-3673-4d22-b5d3-a008aea202bd.in └── f5296bf8-3673-4d22-b5d3-a008aea202bd.out /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /judge/Judge 3 | /judge/Temp/* 4 | 5 | ~* 6 | 7 | *.swp 8 | *.log 9 | log.* 10 | debug.* 11 | 12 | !.gitignore 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | Judge: test.cpp 2 | g++ -o Judge test.cpp --std=c++11 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 从 [KIDx/Judger](https://github.com/KIDx/Judger) 魔改而来,只保留了 cpp 的判题核心。 2 | 3 | 在支持多组测试的基础上,将各组判题结果汇总之后以 JSON 格式输出到 `result.json`。 4 | 5 | 根据 `meta.json` 获取判题组信息,比如: 6 | 7 | ```json 8 | { 9 | "testcases": [ 10 | { "uuid": "a09b1fa7-dd25-4013-a06f-0a04fa857373" }, 11 | { "uuid": "f5296bf8-3673-4d22-b5d3-a008aea202bd" } 12 | ] 13 | } 14 | ``` 15 | 16 | 这之中有两组测试数据。测试数据的名称必须与 uuid 对应,比如: `a09b1fa7-dd25-4013-a06f-0a04fa857373.in` 和 `a09b1fa7-dd25-4013-a06f-0a04fa857373.out` 对应 上例中的第一组测试数据的输入与输出。 17 | 18 | 输出结果的解构为: 19 | 20 | ```json 21 | [ 22 | { 23 | "memory": 2040, 24 | "result": 2, 25 | "time": 0, 26 | "uuid": "a09b1fa7-dd25-4013-a06f-0a04fa857373" 27 | }, 28 | { 29 | "memory": 2040, 30 | "result": 2, 31 | "time": 0, 32 | "uuid": "f5296bf8-3673-4d22-b5d3-a008aea202bd" 33 | } 34 | ] 35 | ``` 36 | 37 | 根据 uuid 得出测试代码在各组数据上的表现情况。 38 | 39 | `result` 字段对应测试结果,相关常量定义在 `judge.h` 中: 40 | 41 | ```cpp 42 | const int OJ_WAIT = 0; //Queue 43 | const int OJ_RUN = 1; //RUN 44 | const int OJ_AC = 2; //AC 45 | const int OJ_PE = 3; //PE 46 | const int OJ_TLE = 4; //TLE 47 | const int OJ_MLE = 5; //MLE 48 | const int OJ_WA = 6; //WA 49 | const int OJ_OLE = 7; //OLE 50 | const int OJ_CE = 8; //CE 51 | const int OJ_RE_SEGV = 9; //SEG Violation 52 | const int OJ_RE_FPU = 10; //float.../0 53 | const int OJ_RE_ABRT = 11; //Abort 54 | const int OJ_RE_UNKNOW = 12; //Unknow 55 | const int OJ_RF = 13; //Restricted Function 56 | const int OJ_SE = 14; //System Error 57 | const int OJ_RE_JAVA = 15; //Java Run Time Exception 58 | ``` 59 | 60 | ## 使用 61 | 62 | 依赖: 63 | 64 | ```bash 65 | apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential 66 | ``` 67 | 68 | 配置: 69 | 70 | 对 `config.ini` 配置,其中将 `sysuser=root` 改为当前你正在使用的用户。 71 | 72 | make 编译: 73 | 74 | ```bash 75 | make 76 | ``` 77 | 78 | 编译成功后: 79 | 80 | ```bash 81 | ./Judge -l 2 -D ./testdata -d temp -t 200 -m 65535 -o 81920 82 | ``` 83 | 84 | -l 对应语言,相关常量定义在 `language.h` 中 85 | 86 | ```cpp 87 | const int LANG_UNKNOWN = 0; 88 | const int LANG_C = 1; 89 | const int LANG_CPP = 2; 90 | const int LANG_JAVA = 3; 91 | ``` 92 | 93 | -D 指明测试数据的文件夹,里面包含了各组测试数据,如 `a09b1fa7-dd25-4013-a06f-0a04fa857373.in` 和 `a09b1fa7-dd25-4013-a06f-0a04fa857373.out`。 94 | 95 | -d 指明临时文件夹,这个文件夹包含了 `ce.txt` (编译错误的信息,如果为空则为没有错误), `result.json` 测试结果,`Main.cpp` / `Main.c` / `Main.java` 测试代码。 96 | 97 | -t 时间限制 允许程序运行的最长时间, 以毫秒为单位, 默认为1000, 1s 98 | 99 | -m 内存限制 允许程序使用的最大内存量, 以KB为单位, 默认为65536, 64MB 100 | 101 | -o 输出限制 允许程序输出的最大数据量, 以KB为单位, 默认为81920, 80MB 102 | 103 | # 注意事项 104 | 105 | 1. config.ini 必须和编译后的 Judge 放在同一个文件夹内 106 | 2. `test.cpp` 790 行左右,删除了对 Java 安全性的检查,因此本评测机对 Java 语言的安全性把控可能达不到要求 107 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [judge] 2 | spj_time_limit=30000 3 | judge_time_limit=60000 4 | stack_size_limit=8192 5 | compile_time_limit=10000 6 | time_limit_addtion=30000 7 | 8 | [system] 9 | log_file=log.txt 10 | sysuser=root 11 | -------------------------------------------------------------------------------- /judge.h: -------------------------------------------------------------------------------- 1 | #ifndef __JUDGE_H__ 2 | #define __JUDGE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "logger.h" 20 | #include "language.h" 21 | 22 | //#define JUDGE_DEBUG 23 | 24 | namespace judge_conf 25 | { 26 | const char config_file[] = "config.ini"; 27 | 28 | std::string log_file = "log.txt"; 29 | 30 | std::string sysuser = "kidx"; 31 | 32 | int judge_time_limit = 40000; 33 | 34 | int stack_size_limit = 8192; 35 | 36 | int compile_time_limit = 60000; 37 | 38 | int spj_time_limit = 10000; 39 | 40 | int time_limit_addtion = 10000; 41 | 42 | void ReadConf() 43 | { 44 | FILE *conf = fopen(config_file, "r"); 45 | if (!conf) return; 46 | enum { UNKNOW, JUDGE, SYSTEM } group; 47 | std::map judm, sysm; 48 | char line[1024], key[512], val[512]; 49 | while (~fscanf(conf, "%s", line)) 50 | { 51 | switch (line[0]) 52 | { 53 | case '[': 54 | if (!strncmp(line+1, "judge", 5)) 55 | group = JUDGE; 56 | else if (!strncmp(line+1, "system", 6)) 57 | group = SYSTEM; 58 | else group = UNKNOW; 59 | case '#': 60 | break; 61 | default: 62 | sscanf(line, "%[^=]=%s", key, val); 63 | switch (group) 64 | { 65 | case JUDGE: 66 | judm[key] = val; 67 | break; 68 | case SYSTEM: 69 | sysm[key] = val; 70 | break; 71 | default: 72 | break; 73 | } 74 | } 75 | } 76 | if (judm.find("judge_time_limit") != judm.end()) 77 | sscanf(judm["judge_time_limit"].c_str(), "%d", &judge_time_limit); 78 | 79 | if (judm.find("stack_size_limit") != judm.end()) 80 | sscanf(judm["stack_size_limit"].c_str(), "%d", &stack_size_limit); 81 | 82 | if (judm.find("compile_time_limit") != judm.end()) 83 | sscanf(judm["compile_time_limit"].c_str(), "%d", &compile_time_limit); 84 | 85 | if (judm.find("spj_time_limit") != judm.end()) 86 | sscanf(judm["spj_time_limit"].c_str(), "%d", &spj_time_limit); 87 | 88 | if (sysm.find("log_file") != sysm.end()) 89 | log_file = sysm["log_file"]; 90 | 91 | if (sysm.find("sysuser") != sysm.end()) 92 | sysuser = sysm["sysuser"]; 93 | } 94 | 95 | const int OJ_WAIT = 0; //Queue 96 | const int OJ_RUN = 1; //RUN 97 | const int OJ_AC = 2; //AC 98 | const int OJ_PE = 3; //PE 99 | const int OJ_TLE = 4; //TLE 100 | const int OJ_MLE = 5; //MLE 101 | const int OJ_WA = 6; //WA 102 | const int OJ_OLE = 7; //OLE 103 | const int OJ_CE = 8; //CE 104 | const int OJ_RE_SEGV = 9; //SEG Violation 105 | const int OJ_RE_FPU = 10; //float.../0 106 | const int OJ_RE_ABRT = 11; //Abort 107 | const int OJ_RE_UNKNOW = 12; //Unknow 108 | const int OJ_RF = 13; //Restricted Function 109 | const int OJ_SE = 14; //System Error 110 | const int OJ_RE_JAVA = 15; //Java Run Time Exception 111 | 112 | const int KILO = 1024; //1K 113 | const int MEGA = KILO*KILO; //1M 114 | const int GIGA = KILO*MEGA; //1G 115 | 116 | const int EXIT_OK = 0; 117 | const int EXIT_UNPRIVILEGED = 1; 118 | const int EXIT_BAD_PARAM = 3; 119 | const int EXIT_VERY_FIRST = 4; 120 | const int EXIT_COMPILE = 6; 121 | const int EXIT_PRE_JUDGE = 9; 122 | const int EXIT_PRE_JUDGE_PTRACE = 10; 123 | const int EXIT_PRE_JUDGE_EXECLP = 11; 124 | const int EXIT_SET_LIMIT = 15; 125 | const int EXIT_SET_SECURITY = 17; 126 | const int EXIT_JUDGE = 21; 127 | const int EXIT_COMPARE = 27; 128 | const int EXIT_ACCESS_SPJ = 29; 129 | const int EXIT_RUNTIME_SPJ = 30; 130 | const int EXIT_COMPARE_SPJ_FORK = 31; 131 | const int EXIT_COMPARE_SPJ_WAIT = 32; 132 | const int EXIT_COMPARE_SPJ_OUT = 33; 133 | const int EXIT_TIMEOUT = 36; 134 | const int EXIT_NO_LOGGER = 38; 135 | const int EXIT_UNKNOWN = 127; 136 | }; 137 | 138 | namespace problem 139 | { 140 | int id = 0; 141 | int lang = 0; 142 | int time_limit = 1000; 143 | int memory_limit = 65536; 144 | int output_limit = 8192; 145 | int result = judge_conf::OJ_SE; 146 | int status; 147 | 148 | long memory_usage = 0; 149 | long time_usage = 0; 150 | bool tc = false; 151 | bool spj = false; 152 | 153 | std::string uid; 154 | std::string temp_dir = "temp"; 155 | std::string data_dir = "data"; 156 | 157 | 158 | std::string source_file; 159 | std::string tc_file; 160 | std::string tc_head; 161 | std::string spj_exe_file; 162 | 163 | std::string input_file; 164 | std::string output_file; 165 | std::string output_file_std; 166 | std::string stdout_file_spj; 167 | 168 | 169 | #ifdef JUDGE_DEBUG 170 | void Problem_debug() 171 | { 172 | LOG_DEBUG("----Problem\tinformation----"); 173 | LOG_DEBUG("Problem spj : %s Problem tc %s", spj?"True":"False", tc?"True":"False"); 174 | LOG_DEBUG("time_limit %d memory_limit %d", time_limit, memory_limit); 175 | LOG_DEBUG("output_limit %d", output_limit); 176 | 177 | LOG_DEBUG("temp_dir %s", temp_dir.c_str()); 178 | LOG_DEBUG("data_dir %s", data_dir.c_str()); 179 | 180 | LOG_DEBUG("input_file %s", input_file.c_str()); 181 | LOG_DEBUG("output_file %s", output_file.c_str()); 182 | LOG_DEBUG("output_file_std %s", output_file_std.c_str()); 183 | } 184 | #endif 185 | }; 186 | 187 | // example:malarm(ITIMER_REAL, judge_conf::judge_time_limit); 188 | int malarm(int which, int milliseconds) 189 | { 190 | struct itimerval it; 191 | it.it_value.tv_sec = milliseconds/1000; 192 | it.it_value.tv_usec = (milliseconds%1000)*1000; 193 | it.it_interval.tv_sec = 0; 194 | it.it_interval.tv_usec = 0; 195 | return setitimer(which, &it, NULL); 196 | } 197 | 198 | #endif 199 | -------------------------------------------------------------------------------- /language.h: -------------------------------------------------------------------------------- 1 | #ifndef _LANGUAGE_H 2 | #define _LANGUAGE_H 3 | 4 | #include 5 | 6 | namespace LanguageSupport 7 | { 8 | 9 | struct LangSupport 10 | { 11 | std::string Name; //编程语言名称 12 | std::string MainFile; //待测程序源码文件 13 | std::string TCfile; //TC模式测试文件的文件名 14 | std::string TChead; //TC模式附加头文件的文件名 15 | const char* const CompileCmd[20]; //编译待评测程序的命令行 16 | const char* const RunCmd[20]; //运行待评测程序的命令行 17 | int TimeFactor; //时间限制的倍数 18 | int MemFactor; //内存限制的倍数 19 | bool VMrunning; //该语言是否以虚拟机方式运行 20 | }; 21 | 22 | const LangSupport UnknownLang = { 23 | "unknown", "NA", "NA", "NA", 24 | {NULL}, 25 | {NULL}, 26 | 0, 0, false 27 | }; 28 | 29 | const LangSupport CLang = { 30 | "c", "Main.c", "tc.c", "tc.h", 31 | #ifdef JUDGE_DEBUG 32 | {"gcc","Main.c","-o","Main", 33 | "-std=c11", "-O2", NULL}, 34 | #else 35 | {"gcc", "Main.c", "-o", "Main", "-Wall", "-lm", 36 | "--static", "-std=c11", "-DONLINE_JUDGE", "-w", NULL }, 37 | #endif 38 | {"./Main", NULL}, 39 | 1, 1, false 40 | }; 41 | 42 | const LangSupport CppLang = { 43 | "c++", "Main.cpp", "tc.cpp", "tc.hpp", 44 | #ifdef JUDGE_DEBUG 45 | {"g++","Main.cpp","-o", 46 | "Main", "-std=c++11", "-O2",NULL}, 47 | #else 48 | { "g++", "Main.cpp", "-o", "Main", "-std=c++11", 49 | "-Wall","-lm", "--static", "-DONLINE_JUDGE", "-w", NULL }, 50 | #endif 51 | {"./Main", NULL}, 52 | 1, 1, false 53 | }; 54 | 55 | const LangSupport JavaLang = { 56 | "java", "Main.java", "tc.java", "tch.java", 57 | #ifdef JUDGE_DEBUG 58 | {"javac", "Main.java", NULL }, 59 | #else 60 | { "javac", "-J-Xms128M", "-J-Xmx512M", "Main.java", NULL }, 61 | #endif 62 | { "java", "-Djava.security.manager", "-Xms128M", "-Xms512M", "-DONLINE_JUDGE=true", "Main", NULL }, 63 | 2, 2, true 64 | }; 65 | }; //End of namespace 66 | 67 | LanguageSupport::LangSupport const *Langs[] = 68 | { 69 | &LanguageSupport::UnknownLang, 70 | &LanguageSupport::CLang, 71 | &LanguageSupport::CppLang, 72 | &LanguageSupport::JavaLang 73 | }; 74 | 75 | namespace judge_conf 76 | { 77 | //OJÓïÑÔ 78 | const int LANG_UNKNOWN = 0; 79 | const int LANG_C = 1; 80 | const int LANG_CPP = 2; 81 | const int LANG_JAVA = 3; 82 | }; 83 | 84 | #endif 85 | 86 | //不要忘了修改okcall.h 87 | -------------------------------------------------------------------------------- /logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * c++ 3 | * A logger for Linux , multiprocess safe 4 | * Author: TT_last 5 | * 6 | * Use: 7 | * //Open log file first 8 | * log_open("log.txt"); 9 | * 10 | * //use it as printf("Hello %s\n", name) 11 | * LOG_DEBUG("Here is a bug %d", line); 12 | * LOG_NOTICE("-_- "); 13 | * LOG_WARNING("..."); 14 | * LOG_BUG("-_-!!!"); 15 | * 16 | * //close log file 17 | * log_close(); 18 | * 19 | * //3 level: DEBUG NOTICE WARNING BUG 20 | */ 21 | 22 | #ifndef __LOGGER_H__ 23 | #define __LOGGER_H__ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | int log_open(const char *filename); 38 | void log_close(); 39 | static int log_write(int level, const char *file, const int line, const char *fmt, ...); 40 | int log_lock(int fd, int cmd, int type, off_t offset, int whence, off_t len); 41 | 42 | const int LOG_DEBUG = 0; 43 | const int LOG_NOTICE = 1; 44 | const int LOG_WARNING = 2; 45 | const int LOG_BUG = 3; 46 | 47 | static char LOG_INFO[][10] = 48 | {"DEBUG", "NOTICE", "WARNING", "BUG"}; 49 | 50 | // Lock file log 51 | #define LOG_WLOCK(fd, offset, whence, len) \ 52 | log_lock(fd, F_SETLKW, F_WRLCK, offset, whence, len) 53 | #define LOG_UNLOCK(fd, offset, whence, len) \ 54 | log_lock(fd, F_SETLK, F_UNLCK, offset, whence, len) 55 | 56 | 57 | #define LOG_DEBUG(x...) log_write(LOG_DEBUG, __FILE__, __LINE__, ##x) 58 | #define LOG_NOTICE(x...) log_write(LOG_NOTICE, __FILE__, __LINE__, ##x) 59 | #define LOG_WARNING(x...) log_write(LOG_WARNING, __FILE__, __LINE__, ##x) 60 | #define LOG_BUG(x...) log_write(LOG_BUG, __FILE__, __LINE__, ##x) 61 | 62 | //log 文件指针 文件名 线程安全 63 | static int log_fd = -1; 64 | static char *log_filename = NULL; 65 | static int log_opened = 0; 66 | 67 | #define log_buffer_size 8192 // 8k 68 | static char log_buffer[log_buffer_size]; 69 | 70 | int log_open(const char *filename) 71 | { 72 | if(log_opened) 73 | { 74 | fprintf(stderr, "logger: log is opened\n"); 75 | return 1; 76 | } 77 | int len = strlen(filename); 78 | log_filename = (char *) malloc(sizeof(char)*len + 1); 79 | strcpy(log_filename, filename); 80 | 81 | log_fd = open(log_filename, O_APPEND|O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); 82 | 83 | if(log_fd == -1) 84 | { 85 | printf("14 0 -38\n"); 86 | fprintf(stderr, "log_file: %s", log_filename); 87 | perror("Can not open log file"); 88 | exit(1); 89 | } 90 | log_opened = 1; 91 | atexit(log_close); 92 | 93 | return 0; 94 | } 95 | 96 | void log_close() 97 | { 98 | if(log_opened) 99 | { 100 | close(log_fd); 101 | free(log_filename); 102 | log_fd = -1; 103 | log_filename = NULL; 104 | log_opened = 0; 105 | } 106 | } 107 | 108 | int log_lock(int fd, int cmd, int type, off_t offset, int whence, off_t len) 109 | { 110 | struct flock lock; 111 | //F_RDLCK F_WRLCK F_UNLCK 112 | lock.l_type = type; 113 | //byte offset, relation to l_whence 114 | lock.l_start = offset; 115 | //SEEK_SET SEEK_CUR SEEK_END 116 | lock.l_whence = whence; 117 | //**bytes (0 means to eof) 118 | lock.l_len = len; 119 | 120 | return ( fcntl(fd, cmd, &lock) ); 121 | } 122 | 123 | int log_write(int level, const char *file, const int line, const char *fmt, ...) 124 | { 125 | if(log_opened == 0) 126 | { 127 | fprintf(stderr, "log_open not called yet\n"); 128 | exit(1); 129 | } 130 | static char buffer[log_buffer_size]; 131 | static char datatime[100]; 132 | static time_t now; 133 | 134 | now = time(NULL); 135 | strftime(datatime, 99, "%Y-%m-%d %H:%M:%S", localtime(&now)); 136 | 137 | va_list ap; 138 | va_start(ap, fmt); 139 | vsnprintf(log_buffer, log_buffer_size, fmt, ap); 140 | va_end(ap); 141 | 142 | size_t count = snprintf(buffer, log_buffer_size, "%s [%s] [%s:%d]--->%s\n", 143 | LOG_INFO[level], datatime, file, line, log_buffer); 144 | 145 | //write(log_fd, buffer, count); 146 | if(LOG_WLOCK(log_fd, 0, SEEK_SET, 0) < 0) 147 | { 148 | perror("lock error"); 149 | exit(1); 150 | } 151 | else 152 | { 153 | if(write(log_fd, buffer, count) < 0) 154 | { 155 | perror("write error"); 156 | exit(1); 157 | } 158 | LOG_UNLOCK(log_fd, 0, SEEK_SET, 0); 159 | } 160 | return 0; 161 | } 162 | 163 | #endif 164 | 165 | /* debug 166 | int main() 167 | { 168 | pid_t pid = fork(); 169 | //if(pid > 0) 170 | //{ 171 | //log_open("log.txt"); 172 | //LOG_WLOCK(log_fd, 0, SEEK_SET, 0); 173 | //sleep(5); 174 | //LOG_UNLOCK(log_fd, 0, SEEK_SET, 0); 175 | //}else 176 | //{ 177 | //sleep(1); 178 | //printf("chirld running\n"); 179 | fork(); fork(); 180 | for(int i = 0;i < 5;i ++) 181 | { 182 | log_open("log.txt"); 183 | LOG_BUG("here is a bug %d", getpid()); 184 | LOG_DEBUG("here is a debug %d", getpid()); 185 | log_close(); 186 | } 187 | //} 188 | return 0; 189 | }*/ 190 | 191 | 192 | -------------------------------------------------------------------------------- /meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "testcases": [ 3 | { "uuid": "a09b1fa7-dd25-4013-a06f-0a04fa857373" }, 4 | { "uuid": "f5296bf8-3673-4d22-b5d3-a008aea202bd" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /okcall.h: -------------------------------------------------------------------------------- 1 | #if __i386__ 2 | #include "okcall_x86.h" 3 | #else 4 | #include "okcall_x64.h" 5 | #endif 6 | -------------------------------------------------------------------------------- /okcall_x64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * syscall 调用的可用次数 3 | * 0 -> 禁止调用 4 | * 1 -> 可以调用次数 5 | * -1 -> 不限制调用次数 6 | */ 7 | 8 | #ifndef __OKCALL__ 9 | #define __OKCALL__ 10 | 11 | #include 12 | #include 13 | #include "judge.h" 14 | 15 | short ok_table[1024] = {0}; 16 | 17 | struct SysCallLimit { 18 | short CallId, Limit; 19 | SysCallLimit(int id, int lim) : CallId(id), Limit(lim) {} 20 | }; 21 | #define Allow(CALL_ID) SysCallLimit((CALL_ID), 22 | #define Calls(CALL_LIM) (CALL_LIM)) 23 | #define EndLimitTable SysCallLimit(-1, -1) 24 | #define INF -1 25 | 26 | SysCallLimit CLimit[] = { 27 | Allow (SYS_access) Calls (1), 28 | Allow (SYS_arch_prctl) Calls (1), 29 | Allow (SYS_brk) Calls (INF), 30 | Allow (SYS_clock_gettime) Calls (INF), 31 | Allow (SYS_close) Calls (1), 32 | Allow (SYS_execve) Calls (1), 33 | Allow (SYS_exit_group) Calls (1), 34 | Allow (SYS_fstat) Calls (3), 35 | Allow (SYS_get_thread_area) Calls (1), 36 | Allow (SYS_gettid) Calls (1), 37 | Allow (SYS_mmap) Calls (INF), 38 | Allow (SYS_mprotect) Calls (16), 39 | Allow (SYS_munmap) Calls (INF), 40 | Allow (SYS_open) Calls (1), 41 | Allow (SYS_read) Calls (INF), 42 | Allow (SYS_readlink) Calls (1), 43 | Allow (SYS_rt_sigprocmask) Calls (1), 44 | Allow (SYS_set_thread_area) Calls (1), 45 | Allow (SYS_tgkill) Calls (2), 46 | Allow (SYS_time) Calls (INF), 47 | Allow (SYS_uname) Calls (1), 48 | Allow (SYS_write) Calls (INF), 49 | Allow (SYS_writev) Calls (INF), 50 | EndLimitTable 51 | }; 52 | 53 | SysCallLimit CppLimit[] = { 54 | Allow (SYS_access) Calls (1), 55 | Allow (SYS_arch_prctl) Calls (1), 56 | Allow (SYS_brk) Calls (INF), 57 | Allow (SYS_clock_gettime) Calls (INF), 58 | Allow (SYS_close) Calls (1), 59 | Allow (SYS_execve) Calls (1), 60 | Allow (SYS_exit_group) Calls (1), 61 | Allow (SYS_futex) Calls (INF), 62 | Allow (SYS_fstat) Calls (3), 63 | Allow (SYS_get_thread_area) Calls (1), 64 | Allow (SYS_gettid) Calls (1), 65 | Allow (SYS_mmap) Calls (INF), 66 | Allow (SYS_mprotect) Calls (16), 67 | Allow (SYS_munmap) Calls (INF), 68 | Allow (SYS_open) Calls (2), 69 | Allow (SYS_read) Calls (INF), 70 | Allow (SYS_readlink) Calls (1), 71 | Allow (SYS_rt_sigprocmask) Calls (1), 72 | Allow (SYS_set_thread_area) Calls (1), 73 | Allow (SYS_set_tid_address) Calls (1), 74 | Allow (SYS_tgkill) Calls (2), 75 | Allow (SYS_time) Calls (INF), 76 | Allow (SYS_uname) Calls (1), 77 | Allow (SYS_write) Calls (INF), 78 | Allow (SYS_writev) Calls (INF), 79 | EndLimitTable 80 | }; 81 | 82 | SysCallLimit JavaLimit[] = { 83 | Allow (SYS_access) Calls (16), 84 | Allow (SYS_arch_prctl) Calls (1), 85 | Allow (SYS_brk) Calls (4), 86 | Allow (SYS_clock_gettime) Calls (INF), 87 | Allow (SYS_clone) Calls (1), 88 | Allow (SYS_close) Calls (INF), 89 | Allow (SYS_execve) Calls (1), 90 | Allow (SYS_exit_group) Calls (1), 91 | Allow (SYS_fstat) Calls (16), 92 | Allow (SYS_futex) Calls (INF), 93 | Allow (SYS_get_thread_area) Calls (1), 94 | Allow (SYS_getdents64) Calls (2), 95 | Allow (SYS_getrlimit) Calls (1), 96 | Allow (SYS_mmap) Calls (INF), 97 | Allow (SYS_mprotect) Calls (INF), 98 | Allow (SYS_munmap) Calls (INF), 99 | Allow (SYS_open) Calls (INF), 100 | Allow (SYS_openat) Calls (1), 101 | Allow (SYS_read) Calls (INF), 102 | Allow (SYS_readlink) Calls (2), 103 | Allow (SYS_rt_sigaction) Calls (2), 104 | Allow (SYS_rt_sigprocmask) Calls (1), 105 | Allow (SYS_set_robust_list) Calls (1), 106 | Allow (SYS_set_thread_area) Calls (1), 107 | Allow (SYS_set_tid_address) Calls (1), 108 | Allow (SYS_stat) Calls (16), 109 | Allow (SYS_time) Calls (INF), 110 | Allow (SYS_uname) Calls (1), 111 | Allow (SYS_write) Calls (INF), 112 | EndLimitTable 113 | }; 114 | 115 | SysCallLimit* TableFor[] = { 116 | NULL, CLimit, CppLimit, JavaLimit, 117 | }; 118 | 119 | //初始化,系统调用限制表格 120 | // 121 | void init_ok_table(int lang) 122 | { 123 | int scall, stimes; 124 | SysCallLimit* table = TableFor[lang]; 125 | memset(ok_table, 0, sizeof(ok_table)); 126 | for (int i = 0; table[i].CallId != -1; ++i) 127 | { 128 | scall = table[i].CallId; 129 | stimes = table[i].Limit; 130 | ok_table[scall] = stimes; 131 | } 132 | //若比赛时出现不应出现的DC,可以临时配置进okcall.cfg中,赛后再修改程序重新编译。 133 | FILE* cfg = fopen("okcall.cfg", "r"); 134 | if (!cfg) return; 135 | while (~fscanf(cfg, "%d := %d", &scall, &stimes)) 136 | { 137 | ok_table[scall] = stimes; 138 | } 139 | fclose(cfg); 140 | } 141 | 142 | static bool in_syscall = true; 143 | 144 | bool is_valid_syscall(int lang, int syscall_id, pid_t child, user_regs_struct regs) 145 | { 146 | in_syscall = !in_syscall; 147 | 148 | //被限制调用的函数,syscall_id = 0 149 | if (ok_table[syscall_id] == 0) 150 | { 151 | long addr; 152 | if (syscall_id == SYS_open) 153 | { 154 | addr = regs.rbx; 155 | union u{unsigned long val;char chars[sizeof(long)];} data; 156 | unsigned long i = 0, j = 0, k = 0; 157 | char filename[1024]; 158 | while (true) 159 | { 160 | data.val = ptrace(PTRACE_PEEKDATA, child, addr+i, NULL); 161 | i += sizeof(long); 162 | for(j = 0; j < sizeof(long) && data.chars[j] > 0 && k < 256; ++j) 163 | { 164 | filename[k++] = data.chars[j]; 165 | } 166 | if (j < sizeof(long) && data.chars[j] == 0) 167 | break; 168 | } 169 | filename[k] = 0; 170 | if (strstr(filename, "/proc/") == filename) 171 | return true; 172 | LOG_NOTICE("syscall_id %d syscall open: %s", syscall_id, filename); 173 | } 174 | return false; 175 | } 176 | else if (ok_table[syscall_id] > 0) 177 | { 178 | if (!in_syscall) 179 | --ok_table[syscall_id]; 180 | } 181 | return true; 182 | } 183 | #endif 184 | -------------------------------------------------------------------------------- /okcall_x86.h: -------------------------------------------------------------------------------- 1 | /* 2 | * syscall 调用的可用次数 3 | * 0 -> 禁止调用 4 | * 1 -> 可以调用次数 5 | * -1 -> 不限制调用次数 6 | */ 7 | 8 | #ifndef __OKCALL__ 9 | #define __OKCALL__ 10 | 11 | #include 12 | #include 13 | #include "judge.h" 14 | 15 | short ok_table[1024] = {0}; 16 | 17 | struct SysCallLimit { 18 | short CallId, Limit; 19 | SysCallLimit(int id, int lim) : CallId(id), Limit(lim) {} 20 | }; 21 | #define Allow(CALL_ID) SysCallLimit((CALL_ID), 22 | #define Calls(CALL_LIM) (CALL_LIM)) 23 | #define EndLimitTable SysCallLimit(-1, -1) 24 | #define INF -1 25 | 26 | SysCallLimit CLimit[] = { 27 | Allow (SYS_access) Calls (1), 28 | Allow (SYS_brk) Calls (INF), 29 | Allow (SYS_clock_gettime) Calls (INF), 30 | Allow (SYS_close) Calls (1), 31 | Allow (SYS_execve) Calls (1), 32 | Allow (SYS_exit_group) Calls (1), 33 | Allow (SYS_fstat64) Calls (4), 34 | Allow (SYS_get_thread_area) Calls (1), 35 | Allow (SYS_gettid) Calls (1), 36 | Allow (SYS_mmap2) Calls (INF), 37 | Allow (SYS_mprotect) Calls (16), 38 | Allow (SYS_munmap) Calls (INF), 39 | Allow (SYS_open) Calls (1), 40 | Allow (SYS_read) Calls (INF), 41 | Allow (SYS_readlink) Calls (1), 42 | Allow (SYS_rt_sigprocmask) Calls (1), 43 | Allow (SYS_set_thread_area) Calls (1), 44 | Allow (SYS_tgkill) Calls (2), 45 | Allow (SYS_time) Calls (INF), 46 | Allow (SYS_uname) Calls (1), 47 | Allow (SYS_write) Calls (INF), 48 | Allow (SYS_writev) Calls (INF), 49 | EndLimitTable 50 | }; 51 | 52 | SysCallLimit CppLimit[] = { 53 | Allow (SYS__llseek) Calls (INF), 54 | Allow (SYS_access) Calls (1), 55 | Allow (SYS_brk) Calls (INF), 56 | Allow (SYS_clock_gettime) Calls (INF), 57 | Allow (SYS_close) Calls (1), 58 | Allow (SYS_execve) Calls (1), 59 | Allow (SYS_exit_group) Calls (1), 60 | Allow (SYS_fstat64) Calls (4), 61 | Allow (SYS_futex) Calls (INF), 62 | Allow (SYS_get_thread_area) Calls (1), 63 | Allow (SYS_gettid) Calls (1), 64 | Allow (SYS_mmap2) Calls (INF), 65 | Allow (SYS_mprotect) Calls (16), 66 | Allow (SYS_munmap) Calls (INF), 67 | Allow (SYS_open) Calls (2), 68 | Allow (SYS_read) Calls (INF), 69 | Allow (SYS_readlink) Calls (1), 70 | Allow (SYS_rt_sigprocmask) Calls (1), 71 | Allow (SYS_set_thread_area) Calls (1), 72 | Allow (SYS_set_tid_address) Calls (1), 73 | Allow (SYS_tgkill) Calls (2), 74 | Allow (SYS_time) Calls (INF), 75 | Allow (SYS_uname) Calls (1), 76 | Allow (SYS_write) Calls (INF), 77 | Allow (SYS_writev) Calls (INF), 78 | EndLimitTable 79 | }; 80 | 81 | SysCallLimit JavaLimit[] = { 82 | Allow (SYS_access) Calls (16), 83 | Allow (SYS_brk) Calls (4), 84 | Allow (SYS_clock_gettime) Calls (INF), 85 | Allow (SYS_clone) Calls (1), 86 | Allow (SYS_close) Calls (INF), 87 | Allow (SYS_execve) Calls (1), 88 | Allow (SYS_exit_group) Calls (1), 89 | Allow (SYS_fstat64) Calls (16), 90 | Allow (SYS_futex) Calls (INF), 91 | Allow (SYS_get_thread_area) Calls (1), 92 | Allow (SYS_getdents64) Calls (2), 93 | Allow (SYS_mmap2) Calls (INF), 94 | Allow (SYS_mprotect) Calls (16), 95 | Allow (SYS_munmap) Calls (INF), 96 | Allow (SYS_open) Calls (INF), 97 | Allow (SYS_openat) Calls (1), 98 | Allow (SYS_read) Calls (INF), 99 | Allow (SYS_readlink) Calls (2), 100 | Allow (SYS_rt_sigaction) Calls (2), 101 | Allow (SYS_rt_sigprocmask) Calls (1), 102 | Allow (SYS_set_robust_list) Calls (1), 103 | Allow (SYS_set_thread_area) Calls (1), 104 | Allow (SYS_set_tid_address) Calls (1), 105 | Allow (SYS_stat64) Calls (INF), 106 | Allow (SYS_time) Calls (INF), 107 | Allow (SYS_ugetrlimit) Calls (1), 108 | Allow (SYS_uname) Calls (1), 109 | Allow (SYS_write) Calls (INF), 110 | EndLimitTable 111 | }; 112 | 113 | SysCallLimit* TableFor[] = { 114 | NULL, CLimit, CppLimit, JavaLimit, 115 | }; 116 | 117 | //初始化,系统调用限制表格 118 | // 119 | void init_ok_table(int lang) 120 | { 121 | int scall, stimes; 122 | SysCallLimit* table = TableFor[lang]; 123 | memset(ok_table,0,sizeof(ok_table)); 124 | for (int i = 0; table[i].CallId != -1; ++i) 125 | { 126 | scall = table[i].CallId; 127 | stimes = table[i].Limit; 128 | ok_table[scall] = stimes; 129 | } 130 | //若比赛时出现不应出现的DC,可以临时配置进okcall.cfg中,赛后再修改程序重新编译。 131 | FILE* cfg = fopen("okcall.cfg","r"); 132 | if (!cfg) return; 133 | while (~fscanf(cfg,"%d := %d", &scall, &stimes)) 134 | { 135 | ok_table[scall] = stimes; 136 | } 137 | fclose(cfg); 138 | } 139 | 140 | static bool in_syscall = true; 141 | 142 | bool is_valid_syscall(int lang,int syscall_id,pid_t child,user_regs_struct regs) 143 | { 144 | in_syscall = !in_syscall; 145 | 146 | //被限制调用的函数,syscall_id = 0 147 | if (ok_table[syscall_id] == 0) 148 | { 149 | long addr; 150 | if (syscall_id == SYS_open) 151 | { 152 | addr = regs.ebx; 153 | union u{unsigned long val;char chars[sizeof(long)];} data; 154 | unsigned long i = 0, j = 0, k = 0; 155 | char filename[1024]; 156 | while (true) 157 | { 158 | data.val = ptrace(PTRACE_PEEKDATA, child, addr+i, NULL); 159 | i += sizeof(long); 160 | for(j = 0; j < sizeof(long) && data.chars[j] > 0 && k < 256; ++j) 161 | { 162 | filename[k++] = data.chars[j]; 163 | } 164 | if (j < sizeof(long) && data.chars[j] == 0) 165 | break; 166 | } 167 | filename[k] = 0; 168 | if (strstr(filename, "/proc/") == filename) 169 | return true; 170 | LOG_NOTICE("syscall_id %d syscall open: %s", syscall_id, filename); 171 | } 172 | return false; 173 | } 174 | else if (ok_table[syscall_id] > 0) 175 | { 176 | if (!in_syscall) 177 | --ok_table[syscall_id]; 178 | } 179 | return true; 180 | } 181 | #endif 182 | -------------------------------------------------------------------------------- /temp/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int main() { 6 | int a, b; 7 | while (cin >> a >> b) { 8 | cout << a + b << endl; 9 | } 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple judge 3 | * Author: TT_last 4 | * 5 | * use: 6 | * ./judge -l 语言 -D 数据目录 -d 临时目录 -t 时间限制 -m 内存限制 -o 输出限制 [-S dd] [-T] 7 | * -l 语言 C=1, C++=2, JAVA=3, C++11=4 8 | * -D 数据目录 包括题号的输入输出文件的目录 9 | * -d 临时目录 judge可以用来存放编译后的文件及其他临时文件的 10 | * -t 时间限制 允许程序运行的最长时间, 以毫秒为单位, 默认为1000, 1s 11 | * -m 内存限制 允许程序使用的最大内存量, 以KB为单位, 默认为65536, 64MB 12 | * -o 输出限制 允许程序输出的最大数据量, 以KB为单位, 默认为8192, 8MB 13 | * -S dd 可选, 如指定此参数, 则表示这是一个Special Judge的题目 14 | * -T 可选,如指定此参数,则表示这个topcoder模式题目,只要写相关头文件 和 相关类+接口就可以了。 15 | * 16 | * 其中-S dd和 -T可以混合用. 17 | * 18 | * 用-S dd,data需要有spj.exe spj.cpp 19 | * 用-T 则相应的需要tc.c tc.cpp tc.java之类的配套 20 | * for example: 21 | * ./judge -l 2 -D data -d temp -t 1000 -m 65536 -o 8192 22 | * 23 | * */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include "judge.h" 44 | #include "okcall.h" 45 | #include "json.hpp" 46 | 47 | #define Max(x, y) (x) > (y) ? (x) : (y) 48 | #define is_space_char(a) ((a == ' ') || (a == '\t') || (a == '\n') || (a == '\r')) 49 | using namespace std; 50 | using json = nlohmann::json; 51 | //#define JUDGE_DEBUG 52 | extern int errno; 53 | const int MAXN = 8192; 54 | 55 | //重构:令语言支持独立于评测逻辑 By Sine 2013_12_01 56 | //In file "language.h" 57 | 58 | void output_result(int result, int memory_usage = 0, int time_usage = 0) 59 | { 60 | //OJ_SE发生时,传入本函数的time_usage即是EXIT错误号,取负数是为了强调和提醒 61 | //此时若memory_usage < 0则为errno,说明错误发生的原因,取负数是为了强调和提醒 62 | //在前台看到System Error时,Time一栏的数值取绝对值就是EXIT错误号,Memory一栏取绝对值则为errno 63 | //OJ_RF发生时,传入本函数的time_usage即是syscall号,取负数是为了强调和提醒 64 | //在前台看到Dangerous Code时,Time一栏的数值取绝对值就是Syscall号 65 | //Bugfix:之前版本评测过程中每处错误发生时一般会直接exit,导致前台status一直Running 66 | //本次fix在所有SE导致的exit前都添加了对本函数的调用,并给出EXIT错误号,解决了此问题,更方便了SE发生时对系统进行调试 67 | //fixed By Sine 2014-03-05 68 | if (result == judge_conf::OJ_SE || result == judge_conf::OJ_RF) time_usage *= -1; 69 | //#ifdef JUDGE_DEBUG 70 | LOG_DEBUG("result: %d, %dKB %dms", result, memory_usage, time_usage); 71 | //#endif 72 | printf("%d %d %d\n", result, memory_usage, time_usage); 73 | } 74 | 75 | void output_result(json j, string path) { 76 | ofstream myfile; 77 | myfile.open(path); 78 | myfile << j.dump(2); 79 | myfile.close(); 80 | // printf("%s\n", j.dump(2).c_str()); 81 | } 82 | 83 | void timeout(int signo) 84 | { 85 | output_result(judge_conf::OJ_SE, 0, judge_conf::EXIT_TIMEOUT); 86 | if (signo == SIGALRM) 87 | exit(judge_conf::EXIT_TIMEOUT); 88 | } 89 | 90 | //normal compare file 91 | void compare_until_nonspace(int &c_std, int &c_usr, FILE *&fd_std, FILE *&fd_usr, int &ret) 92 | { 93 | while((isspace(c_std)) || (isspace(c_usr))) 94 | { 95 | if (c_std != c_usr) 96 | { 97 | if (c_std == EOF || c_usr == EOF) { 98 | return; 99 | } 100 | if (c_std == '\r' && c_usr == '\n') 101 | { 102 | c_std = fgetc(fd_std); 103 | if (c_std != c_usr) 104 | ret = judge_conf::OJ_PE; 105 | } 106 | else 107 | { 108 | ret = judge_conf::OJ_PE; 109 | } 110 | } 111 | if (isspace(c_std)) 112 | c_std = fgetc(fd_std); 113 | if (isspace(c_usr)) 114 | c_usr = fgetc(fd_usr); 115 | } 116 | } 117 | 118 | //合并文件 119 | void addfile(string &main_file, string &tc_file) 120 | { 121 | LOG_DEBUG("TC mode"); 122 | char cc[MAXN+5]; 123 | FILE *sc_fd = fopen(main_file.c_str(), "a+"); 124 | FILE *tc_fd = fopen(tc_file.c_str(), "r"); 125 | if (sc_fd && tc_fd) 126 | { 127 | fputs("\n", sc_fd); 128 | while(fgets(cc, MAXN, tc_fd)) 129 | { 130 | fputs(cc, sc_fd); 131 | } 132 | } 133 | if (sc_fd) fclose(sc_fd); 134 | if (tc_fd) fclose(tc_fd); 135 | } 136 | 137 | int tc_mode() 138 | { 139 | problem::source_file = problem::temp_dir + "/" + Langs[problem::lang]->MainFile; 140 | string syscmd = "mv -f ", source_temp = problem::temp_dir + "/tempfile.txt"; 141 | syscmd += problem::source_file + " " + source_temp; 142 | system(syscmd.c_str()); 143 | addfile(problem::source_file, problem::tc_head); 144 | addfile(problem::source_file, source_temp); 145 | addfile(problem::source_file, problem::tc_file); 146 | return -1; 147 | } 148 | 149 | //特判 150 | int spj_compare_output( 151 | string input_file, //标准输入文件 152 | string output_file, //用户程序的输出文件 153 | string spj_path, //spj路径, change it from exefile to the folder who store the exefile 154 | string file_spj, //spj的输出文件 155 | string output_file_std) 156 | { 157 | #ifdef JUDGE_DEBUG__ 158 | cout<<"inputfile: "<TimeFactor; 366 | problem::memory_limit *= Langs[problem::lang]->MemFactor; 367 | if (problem::tc) 368 | { 369 | problem::tc_file = problem::data_dir + "/" + Langs[problem::lang]->TCfile; 370 | problem::tc_head = problem::data_dir + "/" + Langs[problem::lang]->TChead; 371 | } 372 | if (problem::spj == true) 373 | { 374 | problem::spj_exe_file = "spj.exe"; 375 | problem::stdout_file_spj = "stdout_spj.txt"; 376 | } 377 | judge_conf::judge_time_limit += problem::time_limit; 378 | #ifdef JUDGE_DEBUG 379 | problem::Problem_debug(); 380 | #endif 381 | } 382 | 383 | 384 | //redirect io 385 | void io_redirect() 386 | { 387 | freopen(problem::input_file.c_str(), "r", stdin); 388 | freopen(problem::output_file.c_str(), "w", stdout); 389 | freopen(problem::output_file.c_str(), "w", stderr); 390 | if (stdin == NULL || stdout == NULL) 391 | { 392 | LOG_BUG("error in freopen: stdin(%p) stdout(%p)", stdin, stdout); 393 | exit(judge_conf::EXIT_PRE_JUDGE); 394 | } 395 | #ifdef JUDGE_DEBUG 396 | LOG_DEBUG("io redirece ok!"); 397 | #endif 398 | } 399 | 400 | void set_limit() 401 | { 402 | rlimit lim; 403 | //时间限制 404 | lim.rlim_cur = (problem::time_limit + 999)/1000 + 1; 405 | //LOG_DEBUG("rlim_cur(%d).", lim.rlim_cur); 406 | lim.rlim_max = lim.rlim_cur * 10; 407 | if (setrlimit(RLIMIT_CPU, &lim) < 0) 408 | { 409 | LOG_BUG("error setrlimit for rlimit_cpu"); 410 | exit(judge_conf::EXIT_SET_LIMIT); 411 | } 412 | 413 | //内存大小限制 java 无法运行 414 | /* 415 | if (problem::lang != judge_conf::LANG_JAVA) 416 | { 417 | lim.rlim_max = problem::memory_limit * judge_conf::KILO; 418 | }else lim.rlim_max = (problem::memory_limit + 8192)*judge_conf::KILO; 419 | lim.rlim_cur = lim.rlim_max; 420 | if (setrlimit(RLIMIT_AS, &lim) < 0) 421 | { 422 | LOG_BUG("error setrlimit for rlimit_as"); 423 | exit(judge_conf::EXIT_SET_LIMIT); 424 | }*/ 425 | //设置堆栈的大小 漏掉主程序会SIGSEGV 426 | getrlimit(RLIMIT_STACK, &lim); 427 | int rlim = judge_conf::stack_size_limit*judge_conf::KILO; 428 | //LOG_DEBUG("set stack size : %d", rlim); 429 | if (lim.rlim_max <= rlim) 430 | { 431 | LOG_WARNING("can't set stack size to higher(%d <= %d)", lim.rlim_max, rlim); 432 | } 433 | else 434 | { 435 | lim.rlim_max = rlim; 436 | lim.rlim_cur = rlim; 437 | if (setrlimit(RLIMIT_STACK, &lim) < 0) 438 | { 439 | LOG_WARNING("setrlimit RLIMIT_STACK failed: %s", strerror(errno)); 440 | exit(judge_conf::EXIT_SET_LIMIT); 441 | } 442 | } 443 | 444 | log_close(); 445 | 446 | //输出文件限制 447 | lim.rlim_max = problem::output_limit * judge_conf::KILO; 448 | lim.rlim_cur = lim.rlim_max; 449 | //LOG_BUG("rlim_fsize %d", lim.rlim_max); 450 | if (setrlimit(RLIMIT_FSIZE, &lim) < 0) 451 | { 452 | //LOG_BUG("error setrlimit for rlimit_fsize"); 453 | exit(judge_conf::EXIT_SET_LIMIT); 454 | } 455 | //log_close(); 456 | } 457 | 458 | int Compiler() 459 | { 460 | int status = 0; 461 | pid_t compiler = fork(); 462 | if (compiler < 0) 463 | { 464 | LOG_WARNING("error fork compiler, %d:%s", errno, strerror(errno)); 465 | output_result(judge_conf::OJ_SE, -errno, judge_conf::EXIT_COMPILE); 466 | exit(judge_conf::EXIT_COMPILE); 467 | } 468 | else if (compiler == 0) 469 | { 470 | chdir(problem::temp_dir.c_str()); 471 | freopen("ce.txt", "w", stderr); //编译出错信息 472 | freopen("/dev/null", "w", stdout); //防止编译器在标准输出中输出额外的信息影响评测 473 | malarm(ITIMER_REAL, judge_conf::compile_time_limit); 474 | execvp( Langs[problem::lang]->CompileCmd[0], (char * const *) Langs[problem::lang]->CompileCmd ); 475 | //execvp error 476 | LOG_WARNING("compile evecvp error"); 477 | exit(judge_conf::EXIT_COMPILE); 478 | } 479 | else 480 | { 481 | waitpid(compiler, &status, 0); 482 | return status; 483 | } 484 | } 485 | 486 | const int bufsize = 1024; 487 | int getmemory(pid_t userexe) 488 | { 489 | int ret = 0; 490 | FILE *pd; 491 | char fn[bufsize], buf[bufsize]; 492 | sprintf(fn, "/proc/%d/status", userexe); 493 | pd = fopen(fn, "r"); 494 | while(pd && fgets(buf, bufsize-1, pd)) //这里漏掉了pd & 导致主进程SIGSEGV 495 | { 496 | if (strncmp(buf, "VmPeak:", 7) == 0) 497 | { 498 | sscanf(buf+8, "%d", &ret); 499 | } 500 | } 501 | if (pd) fclose(pd); //9.19 发现漏了,会too many open file 502 | return ret; 503 | } 504 | 505 | bool isInFile(char *filename) 506 | { 507 | int len = strlen(filename); 508 | if (len < 3 || strcmp(filename+len-3, ".in") != 0) 509 | return false; 510 | return true; 511 | } 512 | 513 | json get_meta_json() 514 | { 515 | FILE *ce_msg = fopen("meta.json", "r"); 516 | std::string message = ""; 517 | char tmp[1024]; 518 | while (fgets(tmp, sizeof(tmp), ce_msg)) { 519 | message += tmp; 520 | } 521 | 522 | return json::parse(message); 523 | } 524 | 525 | void sigseg(int) 526 | { 527 | output_result(judge_conf::OJ_SE, 0, judge_conf::EXIT_UNPRIVILEGED); 528 | exit(judge_conf::EXIT_UNPRIVILEGED); 529 | } 530 | 531 | int main(int argc, char *argv[]) 532 | { 533 | judge_conf::ReadConf(); 534 | log_open(judge_conf::log_file.c_str()); 535 | 536 | #ifdef JUDGE_DEBUG 537 | LOG_DEBUG("start judge"); 538 | #endif 539 | if (geteuid() != 0) 540 | { 541 | #ifdef JUDGE_DEBUG 542 | LOG_DEBUG("euid %d != 0, please run as root , or chmod 4755 judge", geteuid()); 543 | #endif 544 | output_result(judge_conf::OJ_SE, 0, judge_conf::EXIT_UNPRIVILEGED); 545 | exit(judge_conf::EXIT_UNPRIVILEGED); 546 | } 547 | parse_arguments(argc, argv); 548 | //设置judge运行时限 549 | if (EXIT_SUCCESS != malarm(ITIMER_REAL, judge_conf::judge_time_limit)) 550 | { 551 | LOG_WARNING("set judge alarm failed, %d : %s", errno, strerror(errno)); 552 | output_result(judge_conf::OJ_SE, 0, judge_conf::EXIT_VERY_FIRST); 553 | exit(judge_conf::EXIT_VERY_FIRST); 554 | } 555 | signal(SIGALRM, timeout); 556 | 557 | //tc 模式 558 | if (problem::tc) 559 | { 560 | tc_mode(); 561 | } 562 | 563 | //编译 564 | int compile_ok = Compiler(); 565 | if (compile_ok != 0) //测试结果OJ_CE 566 | { 567 | output_result(judge_conf::OJ_CE); 568 | exit(judge_conf::EXIT_OK); 569 | } 570 | 571 | json meta = get_meta_json(); 572 | json result = json::array(); 573 | //运行 judge 574 | DIR * dp; 575 | struct dirent *dirp; 576 | dp = opendir(problem::data_dir.c_str()); 577 | if (dp == NULL) 578 | { 579 | LOG_WARNING("error opening dir %s", problem::data_dir.c_str()); 580 | output_result(judge_conf::OJ_SE, 0, judge_conf::EXIT_PRE_JUDGE); 581 | return judge_conf::EXIT_PRE_JUDGE; 582 | } 583 | // char nametmp[1024]; 584 | for (auto& testcase : meta["testcases"]) 585 | { 586 | struct rusage rused; 587 | 588 | // if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) 589 | // continue; 590 | // if (isInFile(dirp->d_name)) 591 | // { 592 | // strcpy(nametmp, dirp->d_name); 593 | // int len = strlen(nametmp); 594 | // nametmp[len-3] = '\0'; 595 | string nametmp = testcase["uuid"]; 596 | problem::input_file = problem::data_dir + "/" + nametmp + ".in"; 597 | problem::output_file_std = problem::data_dir + "/" + nametmp + ".out"; 598 | problem::output_file = problem::temp_dir + "/" + nametmp + ".out"; 599 | 600 | 601 | #ifdef JUDGE_DEBUG 602 | problem::Problem_debug(); 603 | #endif 604 | 605 | pid_t userexe = fork(); 606 | if (userexe < 0) 607 | { 608 | LOG_WARNING("fork for userexe failed, %d:%s", errno, strerror(errno)); 609 | output_result(judge_conf::OJ_SE, -errno, judge_conf::EXIT_PRE_JUDGE); 610 | exit(judge_conf::EXIT_PRE_JUDGE); 611 | } 612 | else if (userexe == 0) 613 | { 614 | 615 | signal(SIGSEGV, sigseg); 616 | //重新定向io 617 | io_redirect(); 618 | 619 | //设置运行根目录、运行用户 620 | //获得运行用户的信息 621 | struct passwd *judge = getpwnam(judge_conf::sysuser.c_str()); 622 | if (judge == NULL) 623 | { 624 | LOG_BUG("no user named %s", judge_conf::sysuser.c_str()); 625 | exit(judge_conf::EXIT_SET_SECURITY); 626 | } 627 | 628 | //切换目录 629 | if (EXIT_SUCCESS != chdir(problem::temp_dir.c_str())) 630 | { 631 | LOG_BUG("chdir failed"); 632 | exit(judge_conf::EXIT_SET_SECURITY); 633 | } 634 | /* char cwd[1024], *tmp = getcwd(cwd, 1024); 635 | if (tmp == NULL) 636 | { //获取当前目录失败 637 | LOG_BUG("getcwd failed"); 638 | exit(judge_conf::EXIT_SET_SECURITY); 639 | }*/ 640 | 641 | //#ifdef JUDEG_DEBUG 642 | //这里现在在fedora下有bug 643 | //设置根目录 644 | /* if (problem::lang == judge_conf::LANG_JAVA) 645 | * { 646 | if (EXIT_SUCCESS != chroot(cwd)) 647 | { 648 | LOG_BUG("chroot failed"); 649 | exit(judge_conf::EXIT_SET_SECURITY); 650 | } 651 | }*/ 652 | 653 | //#endif 654 | //设置有效用户 655 | if (EXIT_SUCCESS != setuid(judge->pw_uid)) 656 | { 657 | LOG_BUG("setuid failed"); 658 | exit(judge_conf::EXIT_SET_SECURITY); 659 | } 660 | 661 | 662 | int user_time_limit = problem::time_limit + judge_conf::time_limit_addtion; 663 | 664 | //设置用户程序的运行实际时间,防止意外情况卡住 665 | if (EXIT_SUCCESS != malarm(ITIMER_REAL, user_time_limit)) 666 | { 667 | LOG_WARNING("malarm failed"); 668 | exit(judge_conf::EXIT_PRE_JUDGE); 669 | } 670 | 671 | //ptrace 监控下面程序 672 | if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) 673 | { 674 | LOG_BUG("ptrace failed"); 675 | exit(judge_conf::EXIT_PRE_JUDGE_PTRACE); 676 | } 677 | 678 | //用setrlimit 设置用户程序的 内存 时间 输出文件大小的限制 679 | //log close for fsize 680 | set_limit(); 681 | 682 | //运行程序 683 | execvp( Langs[problem::lang]->RunCmd[0], (char * const *) Langs[problem::lang]->RunCmd ); 684 | int errsa = errno; 685 | exit(judge_conf::EXIT_PRE_JUDGE_EXECLP); 686 | } 687 | else 688 | { 689 | //父进程监控子进程的状态和系统调用 690 | 691 | int status = 0; 692 | int syscall_id = 0; 693 | struct user_regs_struct regs; 694 | 695 | init_ok_table(problem::lang); 696 | 697 | while (true) 698 | { 699 | if (wait4(userexe, &status, 0, &rused) < 0) 700 | { 701 | LOG_BUG("wait4 failed, %d:%s", errno, strerror(errno)); 702 | output_result(judge_conf::OJ_SE, -errno, judge_conf::EXIT_JUDGE); 703 | exit(judge_conf::EXIT_JUDGE); 704 | } 705 | //如果是退出信号 706 | if (WIFEXITED(status)) 707 | { 708 | //java 返回非0表示出错 709 | if (!Langs[problem::lang]->VMrunning || WEXITSTATUS(status) == EXIT_SUCCESS) 710 | { 711 | int result; 712 | if (problem::spj) 713 | { 714 | //spj 715 | result = spj_compare_output(problem::input_file, 716 | problem::output_file, 717 | problem::data_dir, //problem::spj_exe_file, modif y in 13-11-10 718 | problem::temp_dir + "/" + problem::stdout_file_spj, 719 | problem::output_file_std); 720 | } 721 | else 722 | { 723 | result = compare_output(problem::output_file_std, problem::output_file); 724 | } 725 | //记录结果 726 | if (result != judge_conf::OJ_AC) 727 | { 728 | problem::result = result; 729 | } 730 | else 731 | { 732 | if (problem::result != judge_conf::OJ_PE) 733 | problem::result = result; 734 | } 735 | } 736 | else 737 | { 738 | LOG_BUG("abort quit code: %d\n", WEXITSTATUS(status)); 739 | problem::result = judge_conf::OJ_RE_JAVA; 740 | } 741 | break; 742 | } 743 | 744 | // 收到信号 745 | // RE/TLE/OLE 超过sterlimit限制而结束 746 | // 且过滤掉被ptrace暂停的 SIGTRAP 747 | if (WIFSIGNALED(status) || (WIFSTOPPED(status) && WSTOPSIG(status) != SIGTRAP)) 748 | { 749 | int signo = 0; 750 | if (WIFSIGNALED(status)) 751 | signo = WTERMSIG(status); 752 | else 753 | signo = WSTOPSIG(status); 754 | switch(signo) 755 | { 756 | //TLE 757 | // case SIGALRM: 758 | //LOG_BUG("ALRM"); 759 | case SIGXCPU: 760 | //LOG_BUG("XCPU"); 761 | // case SIGVTALRM: 762 | //LOG_BUG("TALRM"); 763 | case SIGKILL: 764 | //LOG_BUG("KILL"); 765 | problem::result = judge_conf::OJ_TLE; 766 | problem::time_usage = problem::time_limit; 767 | break; 768 | //OLE 769 | case SIGXFSZ: 770 | problem::result = judge_conf::OJ_OLE; 771 | break; 772 | case SIGSEGV: 773 | problem::result = judge_conf::OJ_RE_SEGV; 774 | break; 775 | case SIGABRT: 776 | problem::result = judge_conf::OJ_RE_ABRT; 777 | break; 778 | default: 779 | problem::result = judge_conf::OJ_RE_UNKNOW; 780 | } 781 | //This is a debug 782 | LOG_DEBUG("This is the file: %s\n", problem::input_file.c_str()); 783 | // 784 | ptrace(PTRACE_KILL, userexe); 785 | break; 786 | }//end if 787 | 788 | //MLE 789 | // LOG_DEBUG("old memory: %d , new memory: %d", problem::memory_usage, 790 | // rused.ru_minflt *(getpagesize()/judge_conf::KILO)); 791 | int tempmemory = 0; 792 | 793 | if (Langs[problem::lang]->VMrunning) 794 | { 795 | tempmemory = rused.ru_minflt *(getpagesize()/judge_conf::KILO); 796 | } 797 | else 798 | { 799 | tempmemory = getmemory(userexe); 800 | } 801 | problem::memory_usage = Max(problem::memory_usage, tempmemory); 802 | if (problem::memory_usage > problem::memory_limit) 803 | { 804 | problem::result = judge_conf::OJ_MLE; 805 | ptrace(PTRACE_KILL, userexe); 806 | break; 807 | } 808 | 809 | //检查userexe的syscall 810 | if (ptrace(PTRACE_GETREGS, userexe, 0, ®s) < 0) 811 | { 812 | LOG_BUG("error ptrace ptrace_getregs, %d:%s", errno, strerror(errno)); 813 | output_result(judge_conf::OJ_SE, -errno, judge_conf::EXIT_JUDGE); 814 | exit(judge_conf::EXIT_JUDGE); 815 | } 816 | #ifdef __i386__ 817 | syscall_id = regs.orig_eax; 818 | #else 819 | syscall_id = regs.orig_rax; 820 | #endif 821 | 822 | // 取消对 Java 安全性检查,否则该评测机不能正常运行 823 | if (judge_conf::LANG_JAVA != problem::lang && syscall_id > 0 && (!is_valid_syscall(problem::lang, syscall_id, userexe, regs))) 824 | { 825 | LOG_WARNING("error for syscall %d", syscall_id); 826 | problem::result = judge_conf::OJ_RF; 827 | problem::time_usage = syscall_id; 828 | ptrace(PTRACE_KILL, userexe, NULL, NULL); 829 | break; 830 | } 831 | 832 | if (ptrace(PTRACE_SYSCALL, userexe, NULL, NULL) < 0) 833 | { 834 | LOG_BUG("error ptrace ptrace syscall, %d:%s", errno, strerror(errno)); 835 | output_result(judge_conf::OJ_SE, -errno, judge_conf::EXIT_JUDGE); 836 | exit(judge_conf::EXIT_JUDGE); 837 | } 838 | }//while (true) 839 | }//else userexe end 840 | 841 | // if (problem::result == judge_conf::OJ_RF) break; 842 | 843 | int time_tmp = rused.ru_utime.tv_sec*1000 + rused.ru_utime.tv_usec/1000 844 | + rused.ru_stime.tv_sec*1000 + rused.ru_stime.tv_usec/1000; 845 | if (problem::time_usage < time_tmp) 846 | { 847 | problem::time_usage = time_tmp; 848 | } 849 | 850 | //LOG_DEBUG("time_usage(%d).", problem::time_usage); 851 | 852 | if (problem::time_usage > problem::time_limit) 853 | { 854 | problem::time_usage = problem::time_limit; 855 | problem::result = judge_conf::OJ_TLE; 856 | } 857 | 858 | if (problem::memory_usage > problem::memory_limit) 859 | { 860 | problem::memory_usage = problem::memory_limit; 861 | problem::result = judge_conf::OJ_MLE; 862 | } 863 | 864 | // if (problem::result != judge_conf::OJ_AC && problem::result != judge_conf::OJ_PE) 865 | // { 866 | // break; 867 | // } 868 | json j = json::object(); 869 | j["uuid"] = nametmp; 870 | j["judge"] = problem::result; 871 | j["time"] = problem::time_usage; 872 | j["memory"] = problem::memory_usage; 873 | result.push_back(j); 874 | 875 | // }//if (isInfile()) 876 | 877 | }//end while, next input file 878 | // output_result(problem::result, problem::memory_usage, problem::time_usage); 879 | output_result(result, problem::temp_dir + "/result.json"); 880 | return 0; 881 | } 882 | -------------------------------------------------------------------------------- /testdata/a09b1fa7-dd25-4013-a06f-0a04fa857373.in: -------------------------------------------------------------------------------- 1 | 1 2 2 | -------------------------------------------------------------------------------- /testdata/a09b1fa7-dd25-4013-a06f-0a04fa857373.out: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /testdata/f5296bf8-3673-4d22-b5d3-a008aea202bd.in: -------------------------------------------------------------------------------- 1 | 2 4 2 | -------------------------------------------------------------------------------- /testdata/f5296bf8-3673-4d22-b5d3-a008aea202bd.out: -------------------------------------------------------------------------------- 1 | 6 2 | --------------------------------------------------------------------------------