Example #1
0
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, &regs) < 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;
}
Example #2
0
/*
 * 执行用户提交的程序
 */
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, &regs) < 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);
    }

}