bool judge(const char *input_file, const char *output_file_std, const char *stdout_file_executive, const char *stderr_file_executive) { rusage rused{}; pid_t executor = fork(); // create a child process for executor if (executor < 0) { FM_LOG_FATAL("fork executor failed: %s", strerror(errno)); exit(EXIT_PRE_JUDGE); } else if (executor == 0) { // child process log_add_info("executor"); off_t fsize = file_size(output_file_std); // io redirect, must before set_security_option() io_redirect(input_file, stdout_file_executive, stderr_file_executive); // chroot & setuid set_security_option(); // set memory, time and file size limit etc. set_limit(fsize); // must after set_security_option() FM_LOG_DEBUG("time limit: %d, time limit addtion: %d", oj_solution.time_limit, time_limit_addtion); uint64_t real_time_limit = oj_solution.time_limit + time_limit_addtion; // time fix // set real time alarm if (EXIT_SUCCESS != malarm(ITIMER_REAL, real_time_limit)) { FM_LOG_FATAL("malarm for executor failed: %s", strerror(errno)); exit(EXIT_PRE_JUDGE); } FM_LOG_TRACE("begin execute"); if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) { FM_LOG_FATAL("Trace executor failed: %s", strerror(errno)); exit(EXIT_PRE_JUDGE_PTRACE); } // load program if (oj_solution.lang == LANG_JAVA) { print_executor(EXEC_J); execvp(EXEC_J[0], (char *const *) EXEC_J); } else if (oj_solution.lang == LANG_KOTLIN) { print_executor(EXEC_KT); execvp(EXEC_KT[0], (char *const *) EXEC_KT); } else if (oj_solution.lang == LANG_PYTHON27) { print_executor(EXEC_PY27); #ifdef FAST_JUDGE execvp(EXEC_PY27[0], (char * const *) EXEC_PY27); #else execv(EXEC_PY27[0], (char *const *) EXEC_PY27); #endif } else if (oj_solution.lang == LANG_PYTHON3) { print_executor(EXEC_PY3); #ifdef FAST_JUDGE execvp(EXEC_PY3[0], (char * const *) EXEC_PY3); #else execv(EXEC_PY3[0], (char *const *) EXEC_PY3); #endif } else { execl("./Main", "./Main", NULL); } // exec error FM_LOG_FATAL("exec error"); exit(EXIT_PRE_JUDGE_EXECLP); } else { // Judger int status = 0; user_regs_struct regs{}; stderr = freopen("error.txt", "a+", stderr); init_syscalls(oj_solution.lang); while (true) { if (wait4(executor, &status, 0, &rused) < 0) { FM_LOG_FATAL("wait4 executor failed: %s", strerror(errno)); kill(executor, SIGKILL); exit(EXIT_JUDGE); } if (WIFEXITED(status)) { if ((oj_solution.lang != LANG_JAVA && oj_solution.lang != LANG_KOTLIN) || WEXITSTATUS(status) == EXIT_SUCCESS) { // AC PE WA FM_LOG_TRACE("normal quit"); int result; if (oj_solution.spj) { // use SPJ result = oj_compare_output_spj(input_file, output_file_std, stdout_file_executive, oj_solution.spj_exe_file); } else { // compare file result = oj_compare_output(output_file_std, stdout_file_executive); } // WA if (result == OJ_WA) { oj_solution.result = OJ_WA; } else if (oj_solution.result != OJ_PE) { // previous case is AC oj_solution.result = result; // AC or PE } else /* (oj_solution.result == OJ_PE) */ { // previous case is PE oj_solution.result = OJ_PE; } FM_LOG_NOTICE("case result: %d, problem result: %d", result, oj_solution.result); } else { // not return 0 oj_solution.result = OJ_RE; FM_LOG_NOTICE("abnormal quit, exit_code: %d", WEXITSTATUS(status)); } break; } // RE/TLE/OLE if (WIFSIGNALED(status) || (WIFSTOPPED(status) && WSTOPSIG(status) != SIGTRAP)) { int signo = 0; if (WIFSIGNALED(status)) { signo = WTERMSIG(status); FM_LOG_NOTICE("child process killed by signal %d, %s", signo, strsignal(signo)); } else { signo = WSTOPSIG(status); FM_LOG_NOTICE("child process stopped by signal %d, %s", signo, strsignal(signo)); } switch (signo) { // Ignore case SIGCHLD: oj_solution.result = OJ_AC; break; // TLE case SIGALRM: // alarm() and setitimer(ITIMER_REAL) case SIGVTALRM: // setitimer(ITIMER_VIRTUAL) case SIGXCPU: // exceeds soft processor limit oj_solution.result = OJ_TLE; FM_LOG_TRACE("Time Limit Exceeded: %s", strsignal(signo)); break; // OLE case SIGXFSZ: // exceeds file size limit oj_solution.result = OJ_OLE; FM_LOG_TRACE("Output Limit Exceeded"); break; // RE case SIGSEGV: // segmentation violation case SIGFPE: // any arithmetic exception case SIGBUS: // the process incurs a hardware fault case SIGABRT: // abort() function case SIGKILL: // exceeds hard processor limit default: oj_solution.result = OJ_RE; FILE *fp = fopen(stderr_file_executive, "a+"); if (fp == nullptr) { fprintf(stderr, "%s\n", strsignal(signo)); FM_LOG_WARNING("Runtime Error: %s", strsignal(signo)); } else { fprintf(fp, "%s\n", strsignal(signo)); fclose(fp); } break; } // end of swtich kill(executor, SIGKILL); break; } // end of "if (WIFSIGNALED(status) ...)" oj_solution.memory_usage = std::max(oj_solution.memory_usage, (unsigned long) rused.ru_maxrss); // TODO(power): check why memory exceed too much if (oj_solution.memory_usage > oj_solution.memory_limit) { oj_solution.result = OJ_MLE; kill(executor, SIGKILL); break; } // check syscall if (ptrace(PTRACE_GETREGS, executor, NULL, ®s) < 0) { FM_LOG_FATAL("ptrace(PTRACE_GETREGS) failed: %s", strerror(errno)); kill(executor, SIGKILL); exit(EXIT_JUDGE); } int syscall_id = 0; #ifdef __i386__ syscall_id = (int)regs.orig_eax; #else syscall_id = (int) regs.orig_rax; #endif if (syscall_id > 0 && !is_valid_syscall(syscall_id)) { oj_solution.result = OJ_RF; FM_LOG_FATAL("restricted function, syscall_id: %d", syscall_id); kill(executor, SIGKILL); break; } if (ptrace(PTRACE_SYSCALL, executor, NULL, NULL) < 0) { FM_LOG_FATAL("ptrace(PTRACE_SYSCALL) failed: %s", strerror(errno)); kill(executor, SIGKILL); exit(EXIT_JUDGE); } } // end of while } // end of fork for judge process oj_solution.memory_usage = std::max(oj_solution.memory_usage, (unsigned long) rused.ru_maxrss); if (oj_solution.memory_usage > oj_solution.memory_limit) { oj_solution.result = OJ_MLE; FM_LOG_NOTICE("memory limit exceeded: %d (fault: %d * %d)", oj_solution.memory_usage, rused.ru_minflt, page_size); } oj_solution.time_usage = std::max(oj_solution.time_usage, (unsigned long) rused.ru_utime.tv_sec * 1000 + rused.ru_utime.tv_usec / 1000); if (oj_solution.time_usage > oj_solution.time_limit) { oj_solution.result = OJ_TLE; FM_LOG_TRACE("Time Limit Exceeded"); } if (oj_solution.result != OJ_AC) { if (oj_solution.judge_type == ACM) { FM_LOG_NOTICE("not AC/PE, no need to continue"); } if (oj_solution.result == OJ_TLE) { oj_solution.time_usage = oj_solution.time_limit; } else if (oj_solution.result == OJ_WA) { if (oj_solution.lang == LANG_JAVA) { // TODO: kotlin fix_java_result(stdout_file_executive, stderr_file_executive); } else if ((oj_solution.lang == LANG_PYTHON27 || oj_solution.lang == LANG_PYTHON3) && file_size(stderr_file_executive)) { oj_solution.result = OJ_RE; FM_LOG_TRACE("Runtime Error"); } } return false; } return true; }
/* * 执行用户提交的程序 */ static void judge() { struct rusage rused; pid_t executive = fork(); if (executive < 0) { exit(JUDGE_CONF::EXIT_PRE_JUDGE); } else if (executive == 0) { //子进程,用户程序 FM_LOG_TRACE("Start Judging."); io_redirect(); security_control(); int real_time_limit = PROBLEM::time_limit; if (EXIT_SUCCESS != malarm(ITIMER_REAL, real_time_limit)) { exit(JUDGE_CONF::EXIT_PRE_JUDGE); } set_limit(); if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) { exit(JUDGE_CONF::EXIT_PRE_JUDGE_PTRACE); } if (PROBLEM::lang != JUDGE_CONF::LANG_JAVA){ execl("./a.out", "a.out", NULL); } else { execlp("java", "java", "Main", NULL); } //走到这了说明出错了 exit(JUDGE_CONF::EXIT_PRE_JUDGE_EXECLP); } else { //父进程 int status = 0; //子进程状态 int syscall_id = 0; //系统调用号 struct user_regs_struct regs; //寄存器 init_RF_table(PROBLEM::lang); //初始化系统调用表 while (true) {//循环监控子进程 if (wait4(executive, &status, 0, &rused) < 0) { FM_LOG_WARNING("wait4 failed."); exit(JUDGE_CONF::EXIT_JUDGE); } //自行退出 if (WIFEXITED(status)) { if (PROBLEM::lang != JUDGE_CONF::LANG_JAVA || WEXITSTATUS(status) == EXIT_SUCCESS) { FM_LOG_TRACE("OK, normal quit. All is good."); //PROBLEM::result = JUDGE_CONF::PROCEED; } else { FM_LOG_WARNING("oh, some error occured.Abnormal quit."); PROBLEM::result = JUDGE_CONF::RE; } break; } //被信号终止掉了 if (WIFSIGNALED(status) || (WIFSTOPPED(status) && WSTOPSIG(status) != SIGTRAP)) { //要过滤掉SIGTRAP信号 int signo = 0; if (WIFSIGNALED(status)) { signo = WTERMSIG(status); FM_LOG_WARNING("child signaled by %d : %s", signo, strsignal(signo)); } else { signo = WSTOPSIG(status); FM_LOG_WARNING("child stop by %d : %s\n", signo, strsignal(signo)); } switch (signo) { //TLE case SIGALRM: case SIGXCPU: case SIGVTALRM: case SIGKILL: FM_LOG_TRACE("Well, Time Limit Exeeded"); PROBLEM::time_usage = 0; PROBLEM::memory_usage = 0; PROBLEM::result = JUDGE_CONF::TLE; break; case SIGXFSZ: FM_LOG_TRACE("File Limit Exceeded"); PROBLEM::time_usage = 0; PROBLEM::memory_usage = 0; PROBLEM::result = JUDGE_CONF::OLE; break; case SIGSEGV: case SIGFPE: case SIGBUS: case SIGABRT: //FM_LOG_TRACE("RE了"); PROBLEM::time_usage = 0; PROBLEM::memory_usage = 0; PROBLEM::result = JUDGE_CONF::RE; break; default: //FM_LOG_TRACE("不知道哪儿跪了"); PROBLEM::time_usage = 0; PROBLEM::memory_usage = 0; PROBLEM::result = JUDGE_CONF::RE; break; } ptrace(PTRACE_KILL, executive, NULL, NULL); break; } //MLE PROBLEM::memory_usage = std::max((long int)PROBLEM::memory_usage, rused.ru_minflt * (getpagesize() / JUDGE_CONF::KILO)); if (PROBLEM::memory_usage > PROBLEM::memory_limit) { PROBLEM::time_usage = 0; PROBLEM::memory_usage = 0; PROBLEM::result = JUDGE_CONF::MLE; FM_LOG_TRACE("Well, Memory Limit Exceeded."); ptrace(PTRACE_KILL, executive, NULL, NULL); break; } //获得子进程的寄存器,目的是为了获知其系统调用 if (ptrace(PTRACE_GETREGS, executive, NULL, ®s) < 0) { FM_LOG_WARNING("ptrace PTRACE_GETREGS failed"); exit(JUDGE_CONF::EXIT_JUDGE); } #ifdef __i386__ syscall_id = regs.orig_eax; #else syscall_id = regs.orig_rax; #endif //检查系统调用是否合法 if (syscall_id > 0 && !is_valid_syscall(PROBLEM::lang, syscall_id, executive, regs)) { FM_LOG_WARNING("restricted fuction %d\n", syscall_id); if (syscall_id == SYS_rt_sigprocmask){ FM_LOG_WARNING("The glibc failed."); } else { //FM_LOG_WARNING("%d\n", SYS_write); FM_LOG_WARNING("restricted fuction table"); } PROBLEM::result = JUDGE_CONF::RE; ptrace(PTRACE_KILL, executive, NULL, NULL); break; } if (ptrace(PTRACE_SYSCALL, executive, NULL, NULL) < 0) { FM_LOG_WARNING("ptrace PTRACE_SYSCALL failed."); exit(JUDGE_CONF::EXIT_JUDGE); } } } //这儿关于time_usage和memory_usage计算的有点混乱 //主要是为了减轻web的任务 //只要不是AC,就把time_usage和memory_usage归0 if (PROBLEM::result == JUDGE_CONF::SE){ PROBLEM::time_usage += (rused.ru_utime.tv_sec * 1000 + rused.ru_utime.tv_usec / 1000); PROBLEM::time_usage += (rused.ru_stime.tv_sec * 1000 + rused.ru_stime.tv_usec / 1000); } }