run_program.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <unistd.h>
  5. #include <asm/unistd.h>
  6. #include <sys/ptrace.h>
  7. #include <sys/wait.h>
  8. #include <sys/resource.h>
  9. #include <sys/user.h>
  10. #include <fcntl.h>
  11. #include <cstring>
  12. #include <string>
  13. #include <vector>
  14. #include <set>
  15. #include <argp.h>
  16. #include "uoj_env.h"
  17. using namespace std;
  18. struct RunResult {
  19. int result;
  20. int ust;
  21. int usm;
  22. int exit_code;
  23. RunResult(int _result, int _ust = -1, int _usm = -1, int _exit_code = -1)
  24. : result(_result), ust(_ust), usm(_usm), exit_code(_exit_code) {
  25. if (result != RS_AC) {
  26. ust = -1, usm = -1;
  27. }
  28. }
  29. };
  30. struct RunProgramConfig
  31. {
  32. int time_limit;
  33. int real_time_limit;
  34. int memory_limit;
  35. int output_limit;
  36. int stack_limit;
  37. string input_file_name;
  38. string output_file_name;
  39. string error_file_name;
  40. string result_file_name;
  41. string work_path;
  42. string type;
  43. vector<string> extra_readable_files;
  44. vector<string> extra_writable_files;
  45. bool allow_proc;
  46. bool safe_mode;
  47. bool need_show_trace_details;
  48. string program_name;
  49. string program_basename;
  50. vector<string> argv;
  51. };
  52. int put_result(string result_file_name, RunResult res) {
  53. FILE *f;
  54. if (result_file_name == "stdout") {
  55. f = stdout;
  56. } else if (result_file_name == "stderr") {
  57. f = stderr;
  58. } else {
  59. f = fopen(result_file_name.c_str(), "w");
  60. }
  61. fprintf(f, "%d %d %d %d\n", res.result, res.ust, res.usm, res.exit_code);
  62. if (f != stdout && f != stderr) {
  63. fclose(f);
  64. }
  65. if (res.result == RS_JGF) {
  66. return 1;
  67. } else {
  68. return 0;
  69. }
  70. }
  71. char self_path[PATH_MAX + 1] = {};
  72. #include "run_program_conf.h"
  73. argp_option run_program_argp_options[] =
  74. {
  75. {"tl" , 'T', "TIME_LIMIT" , 0, "Set time limit (in second)" , 1},
  76. {"rtl" , 'R', "TIME_LIMIT" , 0, "Set real time limit (in second)" , 2},
  77. {"ml" , 'M', "MEMORY_LIMIT", 0, "Set memory limit (in mb)" , 3},
  78. {"ol" , 'O', "OUTPUT_LIMIT", 0, "Set output limit (in mb)" , 4},
  79. {"sl" , 'S', "STACK_LIMIT" , 0, "Set stack limit (in mb)" , 5},
  80. {"in" , 'i', "IN" , 0, "Set input file name" , 6},
  81. {"out" , 'o', "OUT" , 0, "Set output file name" , 7},
  82. {"err" , 'e', "ERR" , 0, "Set error file name" , 8},
  83. {"work-path" , 'w', "WORK_PATH" , 0, "Set the work path of the program" , 9},
  84. {"type" , 't', "TYPE" , 0, "Set the program type (for some program such as python)", 10},
  85. {"res" , 'r', "RESULT_FILE" , 0, "Set the file name for outputing the result ", 10},
  86. {"add-readable" , 500, "FILE" , 0, "Add a readable file" , 11},
  87. {"add-writable" , 505, "FILE" , 0, "Add a writable file" , 11},
  88. {"unsafe" , 501, 0 , 0, "Don't check dangerous syscalls" , 12},
  89. {"show-trace-details" , 502, 0 , 0, "Show trace details" , 13},
  90. {"allow-proc" , 503, 0 , 0, "Allow fork, exec... etc." , 14},
  91. {"add-readable-raw" , 504, "FILE" , 0, "Add a readable (don't transform to its real path)" , 15},
  92. {"add-writable-raw" , 506, "FILE" , 0, "Add a writable (don't transform to its real path)" , 15},
  93. {0}
  94. };
  95. error_t run_program_argp_parse_opt (int key, char *arg, struct argp_state *state)
  96. {
  97. RunProgramConfig *config = (RunProgramConfig*)state->input;
  98. switch (key)
  99. {
  100. case 'T':
  101. config->time_limit = atoi(arg);
  102. break;
  103. case 'R':
  104. config->real_time_limit = atoi(arg);
  105. break;
  106. case 'M':
  107. config->memory_limit = atoi(arg);
  108. break;
  109. case 'O':
  110. config->output_limit = atoi(arg);
  111. break;
  112. case 'S':
  113. config->stack_limit = atoi(arg);
  114. break;
  115. case 'i':
  116. config->input_file_name = arg;
  117. break;
  118. case 'o':
  119. config->output_file_name = arg;
  120. break;
  121. case 'e':
  122. config->error_file_name = arg;
  123. break;
  124. case 'w':
  125. config->work_path = realpath(arg);
  126. if (config->work_path.empty()) {
  127. argp_usage(state);
  128. }
  129. break;
  130. case 'r':
  131. config->result_file_name = arg;
  132. break;
  133. case 't':
  134. config->type = arg;
  135. break;
  136. case 500:
  137. config->extra_readable_files.push_back(realpath(arg));
  138. break;
  139. case 501:
  140. config->safe_mode = false;
  141. break;
  142. case 502:
  143. config->need_show_trace_details = true;
  144. break;
  145. case 503:
  146. config->allow_proc = true;
  147. break;
  148. case 504:
  149. config->extra_readable_files.push_back(arg);
  150. break;
  151. case 505:
  152. config->extra_writable_files.push_back(realpath(arg));
  153. break;
  154. case 506:
  155. config->extra_writable_files.push_back(arg);
  156. break;
  157. case ARGP_KEY_ARG:
  158. config->argv.push_back(arg);
  159. for (int i = state->next; i < state->argc; i++) {
  160. config->argv.push_back(state->argv[i]);
  161. }
  162. state->next = state->argc;
  163. break;
  164. case ARGP_KEY_END:
  165. if (state->arg_num == 0) {
  166. argp_usage(state);
  167. }
  168. break;
  169. default:
  170. return ARGP_ERR_UNKNOWN;
  171. }
  172. if (getenv ("UOJ_ALLOW_PROC"))
  173. config->allow_proc = true;
  174. return 0;
  175. }
  176. char run_program_argp_args_doc[] = "program arg1 arg2 ...";
  177. char run_program_argp_doc[] = "run_program: a tool to run program safely";
  178. argp run_program_argp = {
  179. run_program_argp_options,
  180. run_program_argp_parse_opt,
  181. run_program_argp_args_doc,
  182. run_program_argp_doc
  183. };
  184. RunProgramConfig run_program_config;
  185. void parse_args(int argc, char **argv) {
  186. run_program_config.time_limit = 1;
  187. run_program_config.real_time_limit = -1;
  188. run_program_config.memory_limit = 256;
  189. run_program_config.output_limit = 64;
  190. run_program_config.stack_limit = 1024;
  191. run_program_config.input_file_name = "stdin";
  192. run_program_config.output_file_name = "stdout";
  193. run_program_config.error_file_name = "stderr";
  194. run_program_config.work_path = "";
  195. run_program_config.result_file_name = "stdout";
  196. run_program_config.type = "default";
  197. run_program_config.safe_mode = true;
  198. run_program_config.need_show_trace_details = false;
  199. run_program_config.allow_proc = false;
  200. argp_parse(&run_program_argp, argc, argv, ARGP_NO_ARGS | ARGP_IN_ORDER, 0, &run_program_config);
  201. if (run_program_config.real_time_limit == -1)
  202. run_program_config.real_time_limit = run_program_config.time_limit + 2;
  203. run_program_config.stack_limit = min(run_program_config.stack_limit, run_program_config.memory_limit);
  204. if (!run_program_config.work_path.empty()) {
  205. if (chdir(run_program_config.work_path.c_str()) == -1) {
  206. exit(put_result(run_program_config.result_file_name, RS_JGF));
  207. }
  208. }
  209. if (run_program_config.type == "java8" || run_program_config.type == "java11") {
  210. run_program_config.program_name = run_program_config.argv[0];
  211. } else {
  212. run_program_config.program_name = realpath(run_program_config.argv[0]);
  213. }
  214. if (run_program_config.work_path.empty()) {
  215. run_program_config.work_path = dirname(run_program_config.program_name);
  216. run_program_config.program_basename = basename(run_program_config.program_name);
  217. run_program_config.argv[0] = "./" + run_program_config.program_basename;
  218. if (chdir(run_program_config.work_path.c_str()) == -1) {
  219. exit(put_result(run_program_config.result_file_name, RS_JGF));
  220. }
  221. }
  222. if (run_program_config.type == "python2") {
  223. string pre[4] = {"/usr/bin/python2", "-E", "-s", "-B"};
  224. run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 4);
  225. } else if (run_program_config.type == "python3") {
  226. string pre[3] = {"/usr/bin/python3", "-I", "-B"};
  227. run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
  228. } else if (run_program_config.type == "java8") {
  229. string pre[3] = {"/usr/lib/jvm/java-8-openjdk-amd64/bin/java", "-Xmx1024m", "-Xss1024m"};
  230. run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
  231. } else if (run_program_config.type == "java11") {
  232. string pre[3] = {"/usr/lib/jvm/java-11-openjdk-amd64/bin/java", "-Xmx1024m", "-Xss1024m"};
  233. run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
  234. }
  235. }
  236. void set_limit(int r, int rcur, int rmax = -1) {
  237. if (rmax == -1)
  238. rmax = rcur;
  239. struct rlimit l;
  240. if (getrlimit(r, &l) == -1) {
  241. exit(55);
  242. }
  243. l.rlim_cur = rcur;
  244. l.rlim_max = rmax;
  245. if (setrlimit(r, &l) == -1) {
  246. exit(55);
  247. }
  248. }
  249. void run_child() {
  250. set_limit(RLIMIT_CPU, run_program_config.time_limit, run_program_config.real_time_limit);
  251. set_limit(RLIMIT_FSIZE, run_program_config.output_limit << 20);
  252. set_limit(RLIMIT_STACK, run_program_config.stack_limit << 20);
  253. if (run_program_config.input_file_name != "stdin") {
  254. if (freopen(run_program_config.input_file_name.c_str(), "r", stdin) == NULL) {
  255. exit(11);
  256. }
  257. }
  258. if (run_program_config.output_file_name != "stdout" && run_program_config.output_file_name != "stderr") {
  259. if (freopen(run_program_config.output_file_name.c_str(), "w", stdout) == NULL) {
  260. exit(12);
  261. }
  262. }
  263. if (run_program_config.error_file_name != "stderr") {
  264. if (run_program_config.error_file_name == "stdout") {
  265. if (dup2(1, 2) == -1) {
  266. exit(13);
  267. }
  268. } else {
  269. if (freopen(run_program_config.error_file_name.c_str(), "w", stderr) == NULL) {
  270. exit(14);
  271. }
  272. }
  273. if (run_program_config.output_file_name == "stderr") {
  274. if (dup2(2, 1) == -1) {
  275. exit(15);
  276. }
  277. }
  278. }
  279. char *env_path_str = getenv("PATH");
  280. char *env_lang_str = getenv("LANG");
  281. char *env_shell_str = getenv("SHELL");
  282. string env_path = env_path_str ? env_path_str : "";
  283. string env_lang = env_lang_str ? env_lang_str : "";
  284. string env_shell = env_shell_str ? env_shell_str : "";
  285. clearenv();
  286. setenv("USER", "poor_program", 1);
  287. setenv("LOGNAME", "poor_program", 1);
  288. setenv("HOME", run_program_config.work_path.c_str(), 1);
  289. if (env_lang_str) {
  290. setenv("LANG", env_lang.c_str(), 1);
  291. }
  292. if (env_path_str) {
  293. setenv("PATH", env_path.c_str(), 1);
  294. }
  295. setenv("PWD", run_program_config.work_path.c_str(), 1);
  296. if (env_shell_str) {
  297. setenv("SHELL", env_shell.c_str(), 1);
  298. }
  299. char **program_c_argv = new char*[run_program_config.argv.size() + 1];
  300. for (size_t i = 0; i < run_program_config.argv.size(); i++) {
  301. program_c_argv[i] = new char[run_program_config.argv[i].size() + 1];
  302. strcpy(program_c_argv[i], run_program_config.argv[i].c_str());
  303. }
  304. program_c_argv[run_program_config.argv.size()] = NULL;
  305. if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
  306. exit(16);
  307. }
  308. if (execv(program_c_argv[0], program_c_argv) == -1) {
  309. exit(17);
  310. }
  311. }
  312. const int MaxNRPChildren = 50;
  313. struct rp_child_proc {
  314. pid_t pid;
  315. int mode;
  316. };
  317. int n_rp_children;
  318. pid_t rp_timer_pid;
  319. rp_child_proc rp_children[MaxNRPChildren];
  320. int rp_children_pos(pid_t pid) {
  321. for (int i = 0; i < n_rp_children; i++) {
  322. if (rp_children[i].pid == pid) {
  323. return i;
  324. }
  325. }
  326. return -1;
  327. }
  328. int rp_children_add(pid_t pid) {
  329. if (n_rp_children == MaxNRPChildren) {
  330. return -1;
  331. }
  332. rp_children[n_rp_children].pid = pid;
  333. rp_children[n_rp_children].mode = -1;
  334. n_rp_children++;
  335. return 0;
  336. }
  337. void rp_children_del(pid_t pid) {
  338. int new_n = 0;
  339. for (int i = 0; i < n_rp_children; i++) {
  340. if (rp_children[i].pid != pid) {
  341. rp_children[new_n++] = rp_children[i];
  342. }
  343. }
  344. n_rp_children = new_n;
  345. }
  346. void stop_child(pid_t pid) {
  347. kill(pid, SIGKILL);
  348. }
  349. void stop_all() {
  350. kill(rp_timer_pid, SIGKILL);
  351. for (int i = 0; i < n_rp_children; i++) {
  352. kill(rp_children[i].pid, SIGKILL);
  353. }
  354. }
  355. RunResult trace_children() {
  356. rp_timer_pid = fork();
  357. if (rp_timer_pid == -1) {
  358. stop_all();
  359. return RunResult(RS_JGF);
  360. } else if (rp_timer_pid == 0) {
  361. struct timespec ts;
  362. ts.tv_sec = run_program_config.real_time_limit;
  363. ts.tv_nsec = 0;
  364. nanosleep(&ts, NULL);
  365. exit(0);
  366. }
  367. if (run_program_config.need_show_trace_details) {
  368. cerr << "timerpid " << rp_timer_pid << endl;
  369. }
  370. pid_t prev_pid = -1;
  371. while (true) {
  372. int stat = 0;
  373. int sig = 0;
  374. struct rusage ruse;
  375. pid_t pid = wait4(-1, &stat, __WALL, &ruse);
  376. if (run_program_config.need_show_trace_details) {
  377. if (prev_pid != pid) {
  378. cerr << "----------" << pid << "----------" << endl;
  379. }
  380. prev_pid = pid;
  381. }
  382. if (pid == rp_timer_pid) {
  383. if (WIFEXITED(stat) || WIFSIGNALED(stat)) {
  384. stop_all();
  385. return RunResult(RS_TLE);
  386. }
  387. continue;
  388. }
  389. int p = rp_children_pos(pid);
  390. if (p == -1) {
  391. if (run_program_config.need_show_trace_details) {
  392. fprintf(stderr, "new_proc %lld\n", (long long int)pid);
  393. }
  394. if (rp_children_add(pid) == -1) {
  395. stop_child(pid);
  396. stop_all();
  397. return RunResult(RS_DGS);
  398. }
  399. p = n_rp_children - 1;
  400. }
  401. int usertim = ruse.ru_utime.tv_sec * 1000 + ruse.ru_utime.tv_usec / 1000;
  402. int usermem = ruse.ru_maxrss;
  403. if (usertim > run_program_config.time_limit * 1000) {
  404. stop_all();
  405. return RunResult(RS_TLE);
  406. }
  407. if (usermem > run_program_config.memory_limit * 1024) {
  408. stop_all();
  409. return RunResult(RS_MLE);
  410. }
  411. if (WIFEXITED(stat)) {
  412. if (run_program_config.need_show_trace_details) {
  413. fprintf(stderr, "exit : %d\n", WEXITSTATUS(stat));
  414. }
  415. if (rp_children[0].mode == -1) {
  416. stop_all();
  417. return RunResult(RS_JGF, -1, -1, WEXITSTATUS(stat));
  418. } else {
  419. if (pid == rp_children[0].pid) {
  420. stop_all();
  421. return RunResult(RS_AC, usertim, usermem, WEXITSTATUS(stat));
  422. } else {
  423. rp_children_del(pid);
  424. continue;
  425. }
  426. }
  427. }
  428. if (WIFSIGNALED(stat)) {
  429. if (run_program_config.need_show_trace_details) {
  430. fprintf(stderr, "sig exit : %d\n", WTERMSIG(stat));
  431. }
  432. if (pid == rp_children[0].pid) {
  433. switch(WTERMSIG(stat)) {
  434. case SIGXCPU: // nearly impossible
  435. stop_all();
  436. return RunResult(RS_TLE);
  437. case SIGXFSZ:
  438. stop_all();
  439. return RunResult(RS_OLE);
  440. default:
  441. stop_all();
  442. return RunResult(RS_RE);
  443. }
  444. } else {
  445. rp_children_del(pid);
  446. continue;
  447. }
  448. }
  449. if (WIFSTOPPED(stat)) {
  450. sig = WSTOPSIG(stat);
  451. if (rp_children[p].mode == -1) {
  452. if ((p == 0 && sig == SIGTRAP) || (p != 0 && sig == SIGSTOP)) {
  453. if (p == 0) {
  454. int ptrace_opt = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD;
  455. if (run_program_config.safe_mode) {
  456. ptrace_opt |= PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK;
  457. ptrace_opt |= PTRACE_O_TRACEEXEC;
  458. }
  459. if (ptrace(PTRACE_SETOPTIONS, pid, NULL, ptrace_opt) == -1) {
  460. stop_all();
  461. return RunResult(RS_JGF);
  462. }
  463. }
  464. sig = 0;
  465. }
  466. rp_children[p].mode = 0;
  467. } else if (sig == (SIGTRAP | 0x80)) {
  468. if (rp_children[p].mode == 0) {
  469. if (run_program_config.safe_mode) {
  470. if (!check_safe_syscall(pid, run_program_config.need_show_trace_details)) {
  471. stop_all();
  472. return RunResult(RS_DGS);
  473. }
  474. }
  475. rp_children[p].mode = 1;
  476. } else {
  477. if (run_program_config.safe_mode) {
  478. on_syscall_exit(pid, run_program_config.need_show_trace_details);
  479. }
  480. rp_children[p].mode = 0;
  481. }
  482. sig = 0;
  483. } else if (sig == SIGTRAP) {
  484. switch ((stat >> 16) & 0xffff) {
  485. case PTRACE_EVENT_CLONE:
  486. case PTRACE_EVENT_FORK:
  487. case PTRACE_EVENT_VFORK:
  488. sig = 0;
  489. break;
  490. case PTRACE_EVENT_EXEC:
  491. rp_children[p].mode = 1;
  492. sig = 0;
  493. break;
  494. case 0:
  495. break;
  496. default:
  497. stop_all();
  498. return RunResult(RS_JGF);
  499. }
  500. }
  501. if (sig != 0) {
  502. if (run_program_config.need_show_trace_details) {
  503. fprintf(stderr, "sig : %d\n", sig);
  504. }
  505. }
  506. switch(sig) {
  507. case SIGXCPU:
  508. stop_all();
  509. return RunResult(RS_TLE);
  510. case SIGXFSZ:
  511. stop_all();
  512. return RunResult(RS_OLE);
  513. }
  514. }
  515. ptrace(PTRACE_SYSCALL, pid, NULL, sig);
  516. }
  517. }
  518. RunResult run_parent(pid_t pid) {
  519. init_conf(run_program_config);
  520. n_rp_children = 0;
  521. rp_children_add(pid);
  522. return trace_children();
  523. }
  524. int main(int argc, char **argv) {
  525. self_path[readlink("/proc/self/exe", self_path, PATH_MAX)] = '\0';
  526. parse_args(argc, argv);
  527. pid_t pid = fork();
  528. if (pid == -1) {
  529. return put_result(run_program_config.result_file_name, RS_JGF);
  530. } else if (pid == 0) {
  531. run_child();
  532. } else {
  533. return put_result(run_program_config.result_file_name, run_parent(pid));
  534. }
  535. return put_result(run_program_config.result_file_name, RS_JGF);
  536. }