static int exec_seen;
static int partial_line;
-#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
-/* glibc 2.1 or newer -> has lseek64 */
-#define long_seek(f,o,w) lseek64(f,o,w)
-#else
-/* Touching clandestine places in glibc */
-extern loff_t llseek(int fd, loff_t pos, int whence);
-#define long_seek(f,o,w) llseek(f,o,w)
-#endif
+static void die(char *msg, ...) NONRET;
+
+/*** Meta-files ***/
+
+static FILE *metafile;
+
+static void
+meta_open(const char *name)
+{
+ if (!strcmp(name, "-"))
+ {
+ metafile = stdout;
+ return;
+ }
+ metafile = fopen(name, "w");
+ if (!metafile)
+ die("Failed to open metafile '%s'",name);
+}
+
+static void
+meta_close(void)
+{
+ if (metafile && metafile != stdout)
+ fclose(metafile);
+}
+
+static void __attribute__((format(printf,1,2)))
+meta_printf(const char *fmt, ...)
+{
+ if (!metafile)
+ return;
+
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(metafile, fmt, args);
+ va_end(args);
+}
+
+static int total_ms, wall_ms;
+
+static void
+final_stats(struct rusage *rus)
+{
+ struct timeval total, now, wall;
+ timeradd(&rus->ru_utime, &rus->ru_stime, &total);
+ total_ms = total.tv_sec*1000 + total.tv_usec/1000;
+ gettimeofday(&now, NULL);
+ timersub(&now, &start_time, &wall);
+ wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
+
+ meta_printf("time:%d.%03d\n", total_ms/1000, total_ms%1000);
+ meta_printf("time-wall:%d.%03d\n", wall_ms/1000, wall_ms%1000);
+}
+
+/*** Messages and exits ***/
static void NONRET
-box_exit(void)
+box_exit(int rc)
{
if (box_pid > 0)
{
ptrace(PTRACE_KILL, box_pid);
kill(-box_pid, SIGKILL);
kill(box_pid, SIGKILL);
+
+ struct rusage rus;
+ int stat;
+ int p = wait4(box_pid, &stat, 0, &rus);
+ if (p < 0)
+ fprintf(stderr, "UGH: Lost track of the process\n");
+ else
+ final_stats(&rus);
}
- exit(1);
+ meta_close();
+ exit(rc);
}
static void
partial_line = 0;
}
+/* Report an error of the sandbox itself */
static void NONRET __attribute__((format(printf,1,2)))
die(char *msg, ...)
{
va_list args;
va_start(args, msg);
flush_line();
- vfprintf(stderr, msg, args);
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), msg, args);
+ meta_printf("status:XX\nmessage:%s\n", buf);
+ fputs(buf, stderr);
+ fputc('\n', stderr);
+ box_exit(2);
+}
+
+/* Report an error of the program inside the sandbox */
+static void NONRET __attribute__((format(printf,1,2)))
+err(char *msg, ...)
+{
+ va_list args;
+ va_start(args, msg);
+ flush_line();
+ if (msg[0] && msg[1] && msg[2] == ':' && msg[3] == ' ')
+ {
+ meta_printf("status:%c%c\n", msg[0], msg[1]);
+ msg += 4;
+ }
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), msg, args);
+ meta_printf("message:%s\n", buf);
+ fputs(buf, stderr);
fputc('\n', stderr);
- box_exit();
+ box_exit(1);
}
+/* Write a message, but only if in verbose mode */
static void __attribute__((format(printf,1,2)))
msg(char *msg, ...)
{
S(fcntl) = A_YES,
S(fcntl64) = A_YES,
S(mmap) = A_YES,
+ S(mmap2) = A_YES,
S(munmap) = A_YES,
S(ioctl) = A_YES,
S(uname) = A_YES,
S(gettid) = A_YES,
S(set_thread_area) = A_YES,
S(get_thread_area) = A_YES,
+ S(set_tid_address) = A_YES,
S(exit_group) = A_YES,
// Syscalls permitted only in liberal mode
S(rt_sigtimedwait) = A_YES | A_LIBERAL,
S(rt_sigqueueinfo) = A_YES | A_LIBERAL,
S(rt_sigsuspend) = A_YES | A_LIBERAL,
- S(mmap2) = A_YES | A_LIBERAL,
S(_sysctl) = A_YES | A_LIBERAL,
#undef S
};
static int mem_fd;
if (!file_access)
- die("File access forbidden");
+ err("FA: File access forbidden");
if (file_access >= 9)
return;
if (l > remains)
l = remains;
if (!l)
- die("Access to file with name too long");
- if (long_seek(mem_fd, addr, SEEK_SET) < 0)
- die("long_seek(mem): %m");
+ err("FA: Access to file with name too long");
+ if (lseek64(mem_fd, addr, SEEK_SET) < 0)
+ die("lseek64(mem): %m");
remains = read(mem_fd, end, l);
if (remains < 0)
die("read(mem): %m");
if (!remains)
- die("Access to file with name out of memory");
+ err("FA: Access to file with name out of memory");
end += l;
addr += l;
}
act = match_path_rule(&default_path_rules[i], namebuf);
if (act != A_YES)
- die("Forbidden access to file `%s'", namebuf);
+ err("FA: Forbidden access to file `%s'", namebuf);
}
static int
{
case __NR_kill:
if (u->regs.ebx == box_pid)
- die("Committed suicide by signal %d", (int)u->regs.ecx);
+ {
+ meta_printf("exitsig:%d\n", (int)u->regs.ecx);
+ err("SG: Committed suicide by signal %d", (int)u->regs.ecx);
+ }
return 0;
case __NR_tgkill:
if (u->regs.ebx == box_pid && u->regs.ecx == box_pid)
- die("Committed suicide by signal %d", (int)u->regs.edx);
+ {
+ meta_printf("exitsig:%d\n", (int)u->regs.edx);
+ err("SG: Committed suicide by signal %d", (int)u->regs.edx);
+ }
return 0;
default:
return 0;
signal_int(int unused UNUSED)
{
/* Interrupts are fatal, so no synchronization requirements. */
- die("Interrupted");
+ meta_printf("exitsig:%d\n", SIGINT);
+ err("SG: Interrupted");
}
static void
timersub(&now, &start_time, &wall);
wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
if (wall_ms > wall_timeout)
- die("Time limit exceeded (wall clock)");
+ err("TO: Time limit exceeded (wall clock)");
if (verbose > 1)
fprintf(stderr, "[wall time check: %d msec]\n", wall_ms);
}
if (verbose > 1)
fprintf(stderr, "[time check: %d msec]\n", ms);
if (ms > timeout)
- die("Time limit exceeded");
- }
-}
-
-static FILE *metafile;
-
-static void
-metafile_open(const char *name)
-{
- if (!name)
- {
- metafile=NULL;
- return;
+ err("TO: Time limit exceeded");
}
- if (!strcmp(name,"-"))
- {
- metafile=stdout;
- return;
- }
- metafile=fopen(name, "w");
- if (!metafile)
- die("Failed to open metafile '%s'",name);
-}
-
-static void
-metafile_write(double t_total, double t_wall)
-{
- if (!metafile)
- return;
- fprintf(metafile, "time:%0.3f\ntime_wall:%0.3f\n", t_total, t_wall);
}
static void
die("wait4: unknown pid %d exited!", p);
if (WIFEXITED(stat))
{
- struct timeval total, now, wall;
- int total_ms, wall_ms;
box_pid = 0;
+ final_stats(&rus);
if (WEXITSTATUS(stat))
- die("Exited with error status %d", WEXITSTATUS(stat));
- timeradd(&rus.ru_utime, &rus.ru_stime, &total);
- total_ms = total.tv_sec*1000 + total.tv_usec/1000;
- gettimeofday(&now, NULL);
- timersub(&now, &start_time, &wall);
- wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
+ {
+ if (syscall_count)
+ {
+ meta_printf("exitcode:%d\n", WEXITSTATUS(stat));
+ err("RE: Exited with error status %d", WEXITSTATUS(stat));
+ }
+ else
+ {
+ // Internal error happened inside the child process and it has been already reported.
+ box_exit(2);
+ }
+ }
if (timeout && total_ms > timeout)
- die("Time limit exceeded");
+ err("TO: Time limit exceeded");
if (wall_timeout && wall_ms > wall_timeout)
- die("Time limit exceeded (wall clock)");
+ err("TO: Time limit exceeded (wall clock)");
flush_line();
fprintf(stderr, "OK (%d.%03d sec real, %d.%03d sec wall, %d syscalls)\n",
- (int) total.tv_sec, (int) total.tv_usec/1000,
- (int) wall.tv_sec, (int) wall.tv_usec/1000,
+ total_ms/1000, total_ms%1000,
+ wall_ms/1000, wall_ms%1000,
syscall_count);
- metafile_write(total.tv_sec+total.tv_usec/1000000.0,wall.tv_sec+wall.tv_usec/1000000.0);
- exit(0);
+ box_exit(0);
}
if (WIFSIGNALED(stat))
{
box_pid = 0;
- die("Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
+ meta_printf("exitsig:%d\n", WTERMSIG(stat));
+ err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
}
if (WIFSTOPPED(stat))
{
u.regs.orig_eax = 0xffffffff;
if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
die("ptrace(PTRACE_SETREGS): %m");
- die("Forbidden syscall %s", syscall_name(sys, namebuf));
+ err("FO: Forbidden syscall %s", syscall_name(sys, namebuf));
}
}
else /* Syscall return */
ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
}
else
- die("Received signal %d", sig);
+ {
+ meta_printf("exitsig:%d", sig);
+ err("SG: Received signal %d", sig);
+ }
}
else
die("wait4: unknown status %x, giving up!", stat);
-v\t\tBe verbose (use multiple times for even more verbosity)\n\
-w <time>\tSet wall clock time limit (seconds, fractions allowed)\n\
");
- exit(1);
+ exit(2);
}
int
memory_limit = atol(optarg);
break;
case 'M':
- metafile_open(optarg);
+ meta_open(optarg);
break;
case 'o':
redir_stdout = optarg;