From 615e71662aedc011d735fdb1ff5a9995c376edab Mon Sep 17 00:00:00 2001
From: Matt Godbolt <matt@godbolt.org>
Date: Tue, 18 Jun 2019 21:04:40 -0500
Subject: [PATCH] First stab at executing compilers

---
 etc/nsjail/execute.cfg | 118 +++++++++++++++++++++++++++++++++++++++++
 etc/nsjail/sandbox.cfg |   2 +-
 lib/exec.js            |  34 +++++++++++-
 3 files changed, 151 insertions(+), 3 deletions(-)
 create mode 100644 etc/nsjail/execute.cfg

diff --git a/etc/nsjail/execute.cfg b/etc/nsjail/execute.cfg
new file mode 100644
index 000000000..f8a6c0bbd
--- /dev/null
+++ b/etc/nsjail/execute.cfg
@@ -0,0 +1,118 @@
+name: "compiler explorer compiler execution sandbox"
+
+mode: ONCE
+hostname: "ce"
+
+time_limit: 0
+max_cpus: 1
+
+log_level: FATAL
+
+rlimit_as_type: SOFT
+rlimit_cpu_type: SOFT
+rlimit_fsize_type: SOFT
+
+uidmap {
+    inside_id: "0"
+}
+
+gidmap {
+    inside_id: "0"
+}
+
+# must run following as root during system startup
+# cgcreate -a ubuntu:ubuntu -g memory,pids,cpu,net_cls:ce-compile
+cgroup_mem_parent: "ce-compile"
+cgroup_pids_parent: "ce-compile"
+cgroup_net_cls_parent: "ce-compile"
+cgroup_cpu_parent: "ce-compile"
+
+cgroup_mem_max: 1342177280 # 1.25GB
+cgroup_pids_max: 4
+# TODO decide (probably not?)
+#cgroup_cpu_ms_per_sec: 500
+
+mount {
+    src: "/lib"
+    dst: "/lib"
+    is_bind: true
+}
+
+mount {
+    src: "/usr/lib"
+    dst: "/usr/lib"
+    is_bind: true
+}
+
+mount {
+    src: "/lib64"
+    dst: "/lib64"
+    is_bind: true
+    mandatory: false
+}
+
+mount {
+    src: "/lib32"
+    dst: "/lib32"
+    is_bind: true
+    mandatory: false
+}
+
+mount {
+    dst: "/tmp"
+    fstype: "tmpfs"
+    options: "size=20971520,nr_inodes=100" # 20 MiB
+    rw: true
+    noexec: true
+    nodev: true
+    nosuid: true
+}
+
+mount {
+    dst: "/dev"
+    fstype: "tmpfs"
+}
+
+mount {
+    src: "/dev/null"
+    dst: "/dev/null"
+    rw: true
+    is_bind: true
+}
+
+mount {
+    src: "/dev/zero"
+    dst: "/dev/zero"
+    is_bind: true
+}
+
+mount {
+    src: "/dev/urandom"
+    dst: "/dev/random"
+    is_bind: true
+}
+
+mount {
+    src: "/dev/urandom"
+    dst: "/dev/urandom"
+    is_bind: true
+}
+
+mount {
+    dst: "/proc"
+    fstype: "proc"
+}
+
+mount {
+    src: "/opt/compiler-explorer"
+    dst: "/opt/compiler-explorer"
+    is_bind: true
+}
+
+mount {
+    src: "/opt/intel"
+    dst: "/opt/intel"
+    is_bind: true
+}
+
+# TODO: seccomp?
diff --git a/etc/nsjail/sandbox.cfg b/etc/nsjail/sandbox.cfg
index 396f7b942..084acdc1a 100644
--- a/etc/nsjail/sandbox.cfg
+++ b/etc/nsjail/sandbox.cfg
@@ -8,7 +8,7 @@ max_cpus: 1
 
 log_level: FATAL
 
-rlimit_as: 102400 # 100 GiB
+rlimit_as_type: SOFT
 rlimit_cpu_type: SOFT
 rlimit_fsize: 16 # 16 MiB
 rlimit_nofile: 10
diff --git a/lib/exec.js b/lib/exec.js
index 7b88cc396..78f083c1c 100644
--- a/lib/exec.js
+++ b/lib/exec.js
@@ -158,7 +158,7 @@ function sandboxNsjail(command, args, options) {
     const jailingOptions = withNsjailTimeout([
         '--config', 'etc/nsjail/sandbox.cfg',
         '--cwd', '/app',
-        '--bindmount', `${execPath}:/app`,
+        '--bindmount', `${execPath}:/app`
     ]);
 
     if (options.ldPath) {
@@ -403,6 +403,35 @@ function executeFirejail(command, args, options) {
     return executeDirect(firejail, baseOptions.concat(args), options, filenameTransform);
 }
 
+function executeNsjail(command, args, options) {
+    if (needsWine(command)) {
+        throw new Error('WINE not supported');
+    }
+    options = options || {};
+
+    const nsjail = execProps("nsjail");
+    const baseOptions = withNsjailTimeout([], options);
+
+    logger.debug("Regular execution via firejail", {command, args});
+    baseOptions.push('--config');
+    baseOptions.push('etc/nsjail/execute.cfg');
+    let filenameTransform;
+    if (options.customCwd) {
+        baseOptions.push('--bindmount');
+        const privateDir = '/compilation';
+        baseOptions.push(`${options.customCwd}:${privateDir}`);
+        const replacement = options.customCwd;
+        filenameTransform = opt => opt.replace(replacement, privateDir);
+        args = args.map(filenameTransform);
+        delete options.customCwd;
+        baseOptions.push('--cwd');
+        baseOptions.push(privateDir);
+    }
+    baseOptions.push('--');
+    baseOptions.push(command);
+    return executeDirect(nsjail, baseOptions.concat(args), options, filenameTransform);
+}
+
 function executeNone(command, args, options) {
     if (needsWine(command)) {
         return executeWineDirect(command, args, options);
@@ -412,7 +441,8 @@ function executeNone(command, args, options) {
 
 const executeDispatchTable = {
     none: executeNone,
-    firejail: executeFirejail
+    firejail: executeFirejail,
+    nsjail: executeNsjail
 };
 
 function execute(command, args, options) {
-- 
GitLab