Description:
add option -A <opt> to box. This options allow more argument to be explicitly passed to the program We have to use this because if the argument we wish to pass to the program is option (in -? format), box will intepret it as its option and failed accordingly. be noted that, by the definition of getopt, these options will be put after original argument (check the code for more info)
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r186:c8d646326d0a - - 1 file changed: 17 inserted, 4 deleted

@@ -1275,473 +1275,486
1275 1275 }
1276 1276
1277 1277 static void
1278 1278 signal_int(int unused UNUSED)
1279 1279 {
1280 1280 /* Interrupts are fatal, so no synchronization requirements. */
1281 1281 meta_printf("exitsig:%d\n", SIGINT);
1282 1282 err("SG: Interrupted");
1283 1283 }
1284 1284
1285 1285 #define PROC_BUF_SIZE 4096
1286 1286 static void
1287 1287 read_proc_file(char *buf, char *name, int *fdp)
1288 1288 {
1289 1289 int c;
1290 1290
1291 1291 if (!*fdp)
1292 1292 {
1293 1293 sprintf(buf, "/proc/%d/%s", (int) box_pid, name);
1294 1294 *fdp = open(buf, O_RDONLY);
1295 1295 if (*fdp < 0)
1296 1296 die("open(%s): %m", buf);
1297 1297 }
1298 1298 lseek(*fdp, 0, SEEK_SET);
1299 1299 if ((c = read(*fdp, buf, PROC_BUF_SIZE-1)) < 0)
1300 1300 die("read on /proc/$pid/%s: %m", name);
1301 1301 if (c >= PROC_BUF_SIZE-1)
1302 1302 die("/proc/$pid/%s too long", name);
1303 1303 buf[c] = 0;
1304 1304 }
1305 1305
1306 1306 static void
1307 1307 check_timeout(void)
1308 1308 {
1309 1309 if (wall_timeout)
1310 1310 {
1311 1311 struct timeval now, wall;
1312 1312 int wall_ms;
1313 1313 gettimeofday(&now, NULL);
1314 1314 timersub(&now, &start_time, &wall);
1315 1315 wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
1316 1316 if (wall_ms > wall_timeout)
1317 1317 err("TO: Time limit exceeded (wall clock)");
1318 1318 if (verbose > 1)
1319 1319 fprintf(stderr, "[wall time check: %d msec]\n", wall_ms);
1320 1320 }
1321 1321 if (timeout)
1322 1322 {
1323 1323 char buf[PROC_BUF_SIZE], *x;
1324 1324 int utime, stime, ms;
1325 1325 static int proc_stat_fd;
1326 1326 read_proc_file(buf, "stat", &proc_stat_fd);
1327 1327 x = buf;
1328 1328 while (*x && *x != ' ')
1329 1329 x++;
1330 1330 while (*x == ' ')
1331 1331 x++;
1332 1332 if (*x++ != '(')
1333 1333 die("proc stat syntax error 1");
1334 1334 while (*x && (*x != ')' || x[1] != ' '))
1335 1335 x++;
1336 1336 while (*x == ')' || *x == ' ')
1337 1337 x++;
1338 1338 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
1339 1339 die("proc stat syntax error 2");
1340 1340 ms = (utime + stime) * 1000 / ticks_per_sec;
1341 1341 if (verbose > 1)
1342 1342 fprintf(stderr, "[time check: %d msec]\n", ms);
1343 1343 if (ms > timeout && ms > extra_timeout)
1344 1344 err("TO: Time limit exceeded");
1345 1345 }
1346 1346 }
1347 1347
1348 1348 static void
1349 1349 sample_mem_peak(void)
1350 1350 {
1351 1351 /*
1352 1352 * We want to find out the peak memory usage of the process, which is
1353 1353 * maintained by the kernel, but unforunately it gets lost when the
1354 1354 * process exits (it is not reported in struct rusage). Therefore we
1355 1355 * have to sample it whenever we suspect that the process is about
1356 1356 * to exit.
1357 1357 */
1358 1358 char buf[PROC_BUF_SIZE], *x;
1359 1359 static int proc_status_fd;
1360 1360 read_proc_file(buf, "status", &proc_status_fd);
1361 1361
1362 1362 x = buf;
1363 1363 while (*x)
1364 1364 {
1365 1365 char *key = x;
1366 1366 while (*x && *x != ':' && *x != '\n')
1367 1367 x++;
1368 1368 if (!*x || *x == '\n')
1369 1369 break;
1370 1370 *x++ = 0;
1371 1371 while (*x == ' ' || *x == '\t')
1372 1372 x++;
1373 1373
1374 1374 char *val = x;
1375 1375 while (*x && *x != '\n')
1376 1376 x++;
1377 1377 if (!*x)
1378 1378 break;
1379 1379 *x++ = 0;
1380 1380
1381 1381 if (!strcmp(key, "VmPeak"))
1382 1382 {
1383 1383 int peak = atoi(val);
1384 1384 if (peak > mem_peak_kb)
1385 1385 mem_peak_kb = peak;
1386 1386 }
1387 1387 }
1388 1388
1389 1389 if (verbose > 1)
1390 1390 msg("[mem-peak: %u KB]\n", mem_peak_kb);
1391 1391 }
1392 1392
1393 1393 static void
1394 1394 boxkeeper(void)
1395 1395 {
1396 1396 int syscall_count = (filter_syscalls ? 0 : 1);
1397 1397 struct sigaction sa;
1398 1398
1399 1399 is_ptraced = 1;
1400 1400
1401 1401 bzero(&sa, sizeof(sa));
1402 1402 sa.sa_handler = signal_int;
1403 1403 sigaction(SIGINT, &sa, NULL);
1404 1404
1405 1405 gettimeofday(&start_time, NULL);
1406 1406 ticks_per_sec = sysconf(_SC_CLK_TCK);
1407 1407 if (ticks_per_sec <= 0)
1408 1408 die("Invalid ticks_per_sec!");
1409 1409
1410 1410 if (timeout || wall_timeout)
1411 1411 {
1412 1412 sa.sa_handler = signal_alarm;
1413 1413 sigaction(SIGALRM, &sa, NULL);
1414 1414 alarm(1);
1415 1415 }
1416 1416
1417 1417 for(;;)
1418 1418 {
1419 1419 struct rusage rus;
1420 1420 int stat;
1421 1421 pid_t p;
1422 1422 if (timer_tick)
1423 1423 {
1424 1424 check_timeout();
1425 1425 timer_tick = 0;
1426 1426 }
1427 1427 p = wait4(box_pid, &stat, WUNTRACED, &rus);
1428 1428 if (p < 0)
1429 1429 {
1430 1430 if (errno == EINTR)
1431 1431 continue;
1432 1432 die("wait4: %m");
1433 1433 }
1434 1434 if (p != box_pid)
1435 1435 die("wait4: unknown pid %d exited!", p);
1436 1436 if (WIFEXITED(stat))
1437 1437 {
1438 1438 box_pid = 0;
1439 1439 final_stats(&rus);
1440 1440 if (WEXITSTATUS(stat))
1441 1441 {
1442 1442 if (syscall_count)
1443 1443 {
1444 1444 meta_printf("exitcode:%d\n", WEXITSTATUS(stat));
1445 1445 err("RE: Exited with error status %d", WEXITSTATUS(stat));
1446 1446 }
1447 1447 else
1448 1448 {
1449 1449 // Internal error happened inside the child process and it has been already reported.
1450 1450 box_exit(2);
1451 1451 }
1452 1452 }
1453 1453 if (timeout && total_ms > timeout)
1454 1454 err("TO: Time limit exceeded");
1455 1455 if (wall_timeout && wall_ms > wall_timeout)
1456 1456 err("TO: Time limit exceeded (wall clock)");
1457 1457 flush_line();
1458 1458 fprintf(stderr,"OK\n");
1459 1459 box_exit(0);
1460 1460 }
1461 1461 if (WIFSIGNALED(stat))
1462 1462 {
1463 1463 box_pid = 0;
1464 1464 meta_printf("exitsig:%d\n", WTERMSIG(stat));
1465 1465 final_stats(&rus);
1466 1466 err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
1467 1467 }
1468 1468 if (WIFSTOPPED(stat))
1469 1469 {
1470 1470 int sig = WSTOPSIG(stat);
1471 1471 if (sig == SIGTRAP)
1472 1472 {
1473 1473 if (verbose > 2)
1474 1474 msg("[ptrace status %08x] ", stat);
1475 1475 static int stop_count;
1476 1476 if (!stop_count++) /* Traceme request */
1477 1477 msg(">> Traceme request caught\n");
1478 1478 else
1479 1479 err("SG: Breakpoint");
1480 1480 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1481 1481 }
1482 1482 else if (sig == (SIGTRAP | 0x80))
1483 1483 {
1484 1484 if (verbose > 2)
1485 1485 msg("[ptrace status %08x] ", stat);
1486 1486 struct syscall_args a;
1487 1487 static unsigned int sys_tick, last_act;
1488 1488 static arg_t last_sys;
1489 1489 if (++sys_tick & 1) /* Syscall entry */
1490 1490 {
1491 1491 char namebuf[32];
1492 1492 int act;
1493 1493
1494 1494 get_syscall_args(&a, 0);
1495 1495 arg_t sys = a.sys;
1496 1496 msg(">> Syscall %-12s (%08jx,%08jx,%08jx) ", syscall_name(sys, namebuf), (intmax_t) a.arg1, (intmax_t) a.arg2, (intmax_t) a.arg3);
1497 1497 if (!exec_seen)
1498 1498 {
1499 1499 msg("[master] ");
1500 1500 if (sys == NATIVE_NR_execve)
1501 1501 {
1502 1502 exec_seen = 1;
1503 1503 close_user_mem();
1504 1504 }
1505 1505 }
1506 1506 else if ((act = valid_syscall(&a)) >= 0)
1507 1507 {
1508 1508 last_act = act;
1509 1509 syscall_count++;
1510 1510 if (act & A_SAMPLE_MEM)
1511 1511 sample_mem_peak();
1512 1512 }
1513 1513 else
1514 1514 {
1515 1515 /*
1516 1516 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
1517 1517 * so we have to change it to something harmless (e.g., an undefined
1518 1518 * syscall) and make the program continue.
1519 1519 */
1520 1520 set_syscall_nr(&a, ~(arg_t)0);
1521 1521 err("FO: Forbidden syscall %s", syscall_name(sys, namebuf));
1522 1522 }
1523 1523 last_sys = sys;
1524 1524 }
1525 1525 else /* Syscall return */
1526 1526 {
1527 1527 get_syscall_args(&a, 1);
1528 1528 if (a.sys == ~(arg_t)0)
1529 1529 {
1530 1530 /* Some syscalls (sigreturn et al.) do not return a value */
1531 1531 if (!(last_act & A_NO_RETVAL))
1532 1532 err("XX: Syscall does not return, but it should");
1533 1533 }
1534 1534 else
1535 1535 {
1536 1536 if (a.sys != last_sys)
1537 1537 err("XX: Mismatched syscall entry/exit");
1538 1538 }
1539 1539 if (last_act & A_NO_RETVAL)
1540 1540 msg("= ?\n");
1541 1541 else
1542 1542 msg("= %jd\n", (intmax_t) a.result);
1543 1543 }
1544 1544 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1545 1545 }
1546 1546 else if (sig == SIGSTOP)
1547 1547 {
1548 1548 msg(">> SIGSTOP\n");
1549 1549 if (ptrace(PTRACE_SETOPTIONS, box_pid, NULL, (void *) PTRACE_O_TRACESYSGOOD) < 0)
1550 1550 die("ptrace(PTRACE_SETOPTIONS): %m");
1551 1551 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1552 1552 }
1553 1553 else if (sig != SIGXCPU && sig != SIGXFSZ)
1554 1554 {
1555 1555 msg(">> Signal %d\n", sig);
1556 1556 sample_mem_peak(); /* Signal might be fatal, so update mem-peak */
1557 1557 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
1558 1558 }
1559 1559 else
1560 1560 {
1561 1561 meta_printf("exitsig:%d", sig);
1562 1562 err("SG: Received signal %d", sig);
1563 1563 }
1564 1564 }
1565 1565 else
1566 1566 die("wait4: unknown status %x, giving up!", stat);
1567 1567 }
1568 1568 }
1569 1569
1570 1570 static void
1571 1571 box_inside(int argc, char **argv)
1572 1572 {
1573 1573 struct rlimit rl;
1574 1574 char *args[argc+1];
1575 1575
1576 1576 memcpy(args, argv, argc * sizeof(char *));
1577 1577 args[argc] = NULL;
1578 1578 if (set_cwd && chdir(set_cwd))
1579 1579 die("chdir: %m");
1580 1580 if (redir_stdin)
1581 1581 {
1582 1582 close(0);
1583 1583 if (open(redir_stdin, O_RDONLY) != 0)
1584 1584 die("open(\"%s\"): %m", redir_stdin);
1585 1585 }
1586 1586 if (redir_stdout)
1587 1587 {
1588 1588 close(1);
1589 1589 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
1590 1590 die("open(\"%s\"): %m", redir_stdout);
1591 1591 }
1592 1592 if (redir_stderr)
1593 1593 {
1594 1594 close(2);
1595 1595 if (open(redir_stderr, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 2)
1596 1596 die("open(\"%s\"): %m", redir_stderr);
1597 1597 }
1598 1598 else
1599 1599 dup2(1, 2);
1600 1600 setpgrp();
1601 1601
1602 1602 if (memory_limit)
1603 1603 {
1604 1604 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
1605 1605 if (setrlimit(RLIMIT_AS, &rl) < 0)
1606 1606 die("setrlimit(RLIMIT_AS): %m");
1607 1607 }
1608 1608
1609 1609 rl.rlim_cur = rl.rlim_max = (stack_limit ? (rlim_t)stack_limit * 1024 : RLIM_INFINITY);
1610 1610 if (setrlimit(RLIMIT_STACK, &rl) < 0)
1611 1611 die("setrlimit(RLIMIT_STACK): %m");
1612 1612
1613 1613 rl.rlim_cur = rl.rlim_max = 64;
1614 1614 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
1615 1615 die("setrlimit(RLIMIT_NOFILE): %m");
1616 1616
1617 1617 char **env = setup_environment();
1618 1618 if (filter_syscalls)
1619 1619 {
1620 1620 if (ptrace(PTRACE_TRACEME) < 0)
1621 1621 die("ptrace(PTRACE_TRACEME): %m");
1622 1622 /* Trick: Make sure that we are stopped until the boxkeeper wakes up. */
1623 1623 raise(SIGSTOP);
1624 1624 }
1625 1625 execve(args[0], args, env);
1626 1626 die("execve(\"%s\"): %m", args[0]);
1627 1627 }
1628 1628
1629 1629 static void
1630 1630 usage(void)
1631 1631 {
1632 1632 fprintf(stderr, "Invalid arguments!\n");
1633 1633 printf("\
1634 1634 Usage: box [<options>] -- <command> <arguments>\n\
1635 1635 \n\
1636 1636 Options:\n\
1637 1637 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
1638 1638 -c <dir>\tChange directory to <dir> first\n\
1639 1639 -e\t\tInherit full environment of the parent process\n\
1640 1640 -E <var>\tInherit the environment variable <var> from the parent process\n\
1641 1641 -E <var>=<val>\tSet the environment variable <var> to <val>; unset it if <var> is empty\n\
1642 1642 -f\t\tFilter system calls (-ff=very restricted)\n\
1643 1643 -i <file>\tRedirect stdin from <file>\n\
1644 1644 -k <size>\tLimit stack size to <size> KB (default: 0=unlimited)\n\
1645 1645 -m <size>\tLimit address space to <size> KB\n\
1646 1646 -M <file>\tOutput process information to <file> (name:value)\n\
1647 1647 -o <file>\tRedirect stdout to <file>\n\
1648 1648 -p <path>\tPermit access to the specified path (or subtree if it ends with a `/')\n\
1649 1649 -p <path>=<act>\tDefine action for the specified path (<act>=yes/no)\n\
1650 1650 -r <file>\tRedirect stderr to <file>\n\
1651 1651 -s <sys>\tPermit the specified syscall (be careful)\n\
1652 1652 -s <sys>=<act>\tDefine action for the specified syscall (<act>=yes/no/file)\n\
1653 1653 -t <time>\tSet run time limit (seconds, fractions allowed)\n\
1654 1654 -T\t\tAllow syscalls for measuring run time\n\
1655 1655 -v\t\tBe verbose (use multiple times for even more verbosity)\n\
1656 1656 -w <time>\tSet wall clock time limit (seconds, fractions allowed)\n\
1657 1657 -x <time>\tSet extra timeout, before which a timing-out program is not yet killed,\n\
1658 1658 \t\tso that its real execution time is reported (seconds, fractions allowed)\n\
1659 + -A <opt>\tPass <opt> as additional argument to the <command>\n\
1660 + \t\tBe noted that this option will be appended after <arguments> respectively\n\
1659 1661 ");
1660 1662 exit(2);
1661 1663 }
1662 1664
1663 1665 int
1664 1666 main(int argc, char **argv)
1665 1667 {
1666 1668 int c;
1667 1669 uid_t uid;
1670 + char **prog_argv = xmalloc(sizeof(char*) * argc);
1671 + int prog_argc = 0;
1668 1672
1669 - while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:")) >= 0)
1673 + while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:A:")) >= 0)
1670 1674 switch (c)
1671 1675 {
1672 1676 case 'a':
1673 1677 file_access = atol(optarg);
1674 1678 break;
1675 1679 case 'c':
1676 1680 set_cwd = optarg;
1677 1681 break;
1678 1682 case 'e':
1679 1683 pass_environ = 1;
1680 1684 break;
1681 1685 case 'E':
1682 1686 if (!set_env_action(optarg))
1683 1687 usage();
1684 1688 break;
1685 1689 case 'f':
1686 1690 filter_syscalls++;
1687 1691 break;
1688 1692 case 'k':
1689 1693 stack_limit = atol(optarg);
1690 1694 break;
1691 1695 case 'i':
1692 1696 redir_stdin = optarg;
1693 1697 break;
1694 1698 case 'm':
1695 1699 memory_limit = atol(optarg);
1696 1700 break;
1697 1701 case 'M':
1698 1702 meta_open(optarg);
1699 1703 break;
1700 1704 case 'o':
1701 1705 redir_stdout = optarg;
1702 1706 break;
1703 1707 case 'p':
1704 1708 if (!set_path_action(optarg))
1705 1709 usage();
1706 1710 break;
1707 1711 case 'r':
1708 1712 redir_stderr = optarg;
1709 1713 break;
1710 1714 case 's':
1711 1715 if (!set_syscall_action(optarg))
1712 1716 usage();
1713 1717 break;
1714 1718 case 't':
1715 1719 timeout = 1000*atof(optarg);
1716 1720 break;
1717 1721 case 'T':
1718 1722 syscall_action[__NR_times] = A_YES;
1719 1723 break;
1720 1724 case 'v':
1721 1725 verbose++;
1722 1726 break;
1723 1727 case 'w':
1724 1728 wall_timeout = 1000*atof(optarg);
1725 1729 break;
1726 1730 case 'x':
1727 1731 extra_timeout = 1000*atof(optarg);
1732 + case 'A':
1733 + prog_argv[prog_argc++] = strdup(optarg);
1734 + break;
1728 1735 break;
1729 1736 default:
1730 1737 usage();
1731 1738 }
1732 1739 if (optind >= argc)
1733 1740 usage();
1734 1741
1735 1742 sanity_check();
1736 1743 uid = geteuid();
1737 1744 if (setreuid(uid, uid) < 0)
1738 1745 die("setreuid: %m");
1739 1746 box_pid = fork();
1740 1747 if (box_pid < 0)
1741 1748 die("fork: %m");
1742 - if (!box_pid)
1743 - box_inside(argc-optind, argv+optind);
1744 - else
1749 + if (!box_pid) {
1750 + int real_argc = prog_argc + argc - optind;
1751 + char **real_argv = xmalloc(sizeof(char*) * (real_argc));
1752 + for (int i = 0;i < argc-optind;i++)
1753 + real_argv[i] = strdup(argv[i+optind]);
1754 + for (int i = 0;i < prog_argc;i++)
1755 + real_argv[argc - optind + i] = strdup(prog_argv[i]);
1756 + box_inside(real_argc, real_argv);
1757 + } else
1745 1758 boxkeeper();
1746 1759 die("Internal error: fell over edge of the world");
1747 1760 }
You need to be logged in to leave comments. Login now