+static const char * const syscall_tab[] = {
+#include "syscall-table.h"
+};
+#define NUM_SYSCALLS (sizeof(syscall_tab)/sizeof(syscall_tab[0]))
+#define NUM_ACTIONS (NUM_SYSCALLS+64)
+
+enum action {
+ SC_DEFAULT, // Use the default action
+ SC_NO, // Always forbid
+ SC_YES, // Always permit
+ SC_FILENAME, // Permit if arg1 is a known filename
+ SC_LIBERAL = 128, // Valid only in liberal mode
+};
+
+static unsigned char syscall_action[NUM_ACTIONS] = {
+#define S(x) [__NR_##x]
+
+ // Syscalls permitted for specific file names
+ S(open) = SC_FILENAME,
+ S(creat) = SC_FILENAME,
+ S(unlink) = SC_FILENAME,
+ S(oldstat) = SC_FILENAME,
+ S(access) = SC_FILENAME,
+ S(oldlstat) = SC_FILENAME,
+ S(truncate) = SC_FILENAME,
+ S(stat) = SC_FILENAME,
+ S(lstat) = SC_FILENAME,
+ S(truncate64) = SC_FILENAME,
+ S(stat64) = SC_FILENAME,
+ S(lstat64) = SC_FILENAME,
+ S(readlink) = SC_FILENAME,
+
+ // Syscalls permitted always
+ S(exit) = SC_YES,
+ S(read) = SC_YES,
+ S(write) = SC_YES,
+ S(close) = SC_YES,
+ S(lseek) = SC_YES,
+ S(getpid) = SC_YES,
+ S(getuid) = SC_YES,
+ S(oldfstat) = SC_YES,
+ S(dup) = SC_YES,
+ S(brk) = SC_YES,
+ S(getgid) = SC_YES,
+ S(geteuid) = SC_YES,
+ S(getegid) = SC_YES,
+ S(dup2) = SC_YES,
+ S(ftruncate) = SC_YES,
+ S(fstat) = SC_YES,
+ S(personality) = SC_YES,
+ S(_llseek) = SC_YES,
+ S(readv) = SC_YES,
+ S(writev) = SC_YES,
+ S(getresuid) = SC_YES,
+#ifdef __NR_pread64
+ S(pread64) = SC_YES,
+ S(pwrite64) = SC_YES,
+#else
+ S(pread) = SC_YES,
+ S(pwrite) = SC_YES,
+#endif
+ S(ftruncate64) = SC_YES,
+ S(fstat64) = SC_YES,
+ S(fcntl) = SC_YES,
+ S(fcntl64) = SC_YES,
+ S(mmap) = SC_YES,
+ S(munmap) = SC_YES,
+ S(ioctl) = SC_YES,
+ S(uname) = SC_YES,
+ S(gettid) = SC_YES,
+ S(set_thread_area) = SC_YES,
+ S(get_thread_area) = SC_YES,
+ S(exit_group) = SC_YES,
+
+ // Syscalls permitted only in liberal mode
+ S(time) = SC_YES | SC_LIBERAL,
+ S(alarm) = SC_YES | SC_LIBERAL,
+ S(pause) = SC_YES | SC_LIBERAL,
+ S(signal) = SC_YES | SC_LIBERAL,
+ S(fchmod) = SC_YES | SC_LIBERAL,
+ S(sigaction) = SC_YES | SC_LIBERAL,
+ S(sgetmask) = SC_YES | SC_LIBERAL,
+ S(ssetmask) = SC_YES | SC_LIBERAL,
+ S(sigsuspend) = SC_YES | SC_LIBERAL,
+ S(sigpending) = SC_YES | SC_LIBERAL,
+ S(getrlimit) = SC_YES | SC_LIBERAL,
+ S(getrusage) = SC_YES | SC_LIBERAL,
+ S(ugetrlimit) = SC_YES | SC_LIBERAL,
+ S(gettimeofday) = SC_YES | SC_LIBERAL,
+ S(select) = SC_YES | SC_LIBERAL,
+ S(readdir) = SC_YES | SC_LIBERAL,
+ S(setitimer) = SC_YES | SC_LIBERAL,
+ S(getitimer) = SC_YES | SC_LIBERAL,
+ S(sigreturn) = SC_YES | SC_LIBERAL,
+ S(mprotect) = SC_YES | SC_LIBERAL,
+ S(sigprocmask) = SC_YES | SC_LIBERAL,
+ S(getdents) = SC_YES | SC_LIBERAL,
+ S(getdents64) = SC_YES | SC_LIBERAL,
+ S(_newselect) = SC_YES | SC_LIBERAL,
+ S(fdatasync) = SC_YES | SC_LIBERAL,
+ S(mremap) = SC_YES | SC_LIBERAL,
+ S(poll) = SC_YES | SC_LIBERAL,
+ S(getcwd) = SC_YES | SC_LIBERAL,
+ S(nanosleep) = SC_YES | SC_LIBERAL,
+ S(rt_sigreturn) = SC_YES | SC_LIBERAL,
+ S(rt_sigaction) = SC_YES | SC_LIBERAL,
+ S(rt_sigprocmask) = SC_YES | SC_LIBERAL,
+ S(rt_sigpending) = SC_YES | SC_LIBERAL,
+ S(rt_sigtimedwait) = SC_YES | SC_LIBERAL,
+ S(rt_sigqueueinfo) = SC_YES | SC_LIBERAL,
+ S(rt_sigsuspend) = SC_YES | SC_LIBERAL,
+ S(mmap2) = SC_YES | SC_LIBERAL,
+ S(_sysctl) = SC_YES | SC_LIBERAL,
+#undef S
+};
+
+static const char *
+syscall_name(unsigned int id, char *buf)
+{
+ if (id < NUM_SYSCALLS && syscall_tab[id])
+ return syscall_tab[id];
+ else
+ {
+ sprintf(buf, "#%d", id);
+ return buf;
+ }
+}
+
+static int
+syscall_by_name(char *name)
+{
+ for (unsigned int i=0; i<sizeof(syscall_tab)/sizeof(syscall_tab[0]); i++)
+ if (syscall_tab[i] && !strcmp(syscall_tab[i], name))
+ return i;
+ if (name[0] == '#')
+ name++;
+ if (!*name)
+ return -1;
+ char *ep;
+ unsigned long l = strtoul(name, &ep, 0);
+ if (*ep)
+ return -1;
+ if (l >= NUM_ACTIONS)
+ return NUM_ACTIONS;
+ return l;
+}
+
+static int
+set_action(char *a)
+{
+ char *sep = strchr(a, '=');
+ enum action act = SC_YES;
+ if (sep)
+ {
+ *sep++ = 0;
+ if (!strcmp(sep, "yes"))
+ act = SC_YES;
+ else if (!strcmp(sep, "no"))
+ act = SC_NO;
+ else if (!strcmp(sep, "file"))
+ act = SC_FILENAME;
+ else
+ return 0;
+ }
+
+ int sys = syscall_by_name(a);
+ if (sys < 0)
+ die("Unknown syscall `%s'", a);
+ if (sys >= (int)NUM_ACTIONS)
+ die("Syscall `%s' out of range", a);
+ syscall_action[sys] = act;
+ return 1;
+}
+
+struct path_rule {
+ char *path;
+ enum action action;
+ struct path_rule *next;
+};
+
+static struct path_rule default_path_rules[] = {
+ { "/etc/", SC_YES },
+ { "/lib/", SC_YES },
+ { "/usr/lib/", SC_YES },
+ { "/opt/lib/", SC_YES },
+ { "/usr/share/zoneinfo/", SC_YES },
+ { "/usr/share/locale/", SC_YES },
+ { "/dev/null", SC_YES },
+ { "/dev/zero", SC_YES },
+ { "/proc/meminfo", SC_YES },
+ { "/proc/self/stat", SC_YES },
+ { "/proc/self/exe", SC_YES }, // Needed by FPC 2.0.x runtime
+};
+
+static enum action
+match_path_rule(struct path_rule *r, char *path)
+{
+ char *rr = r->path;
+ while (*rr)
+ if (*rr++ != *path++)
+ {
+ if (rr[-1] == '/' && !path[-1])
+ break;
+ return SC_DEFAULT;
+ }
+ if (rr > r->path && rr[-1] != '/' && *path)
+ return SC_DEFAULT;
+ return r->action;
+}
+