Repositories / jai.git

jai.git

Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git

Branch

Give jails a writable /dev/shm (suggestion from arjunguha)

Author
David Mazieres <dm@uun.org>
Date
2026-04-02 12:43:45 -0700
Commit
2f0573b7c488be2cbb4f23a20d089bf11320c4a9
jai.cc
index a1f393b..c0138ef 100644
--- a/jai.cc
+++ b/jai.cc
@@ -319,32 +319,42 @@ Config::make_home_overlay()
 }
 
 Fd
-Config::make_private_tmp()
+Config::make_private_tmproot()
 {
   auto r = lock_or_validate_file(
       run_jai_user(), "tmp", O_RDONLY | O_DIRECTORY,
       [](int fd) { return is_mountpoint(fd); }, ".lock");
   if (r)
-    return ensure_dir(**r, sandbox_name_, 01777, kNoFollow);
+    return std::move(*r);
 
   Fd tmp = ensure_dir(run_jai_user(), "tmp", 0755, kFollow);
   if (!is_mountpoint(*tmp)) {
-    xmnt_move(*make_tmpfs("jai-tmp", "gid", "0", "mode", "0755", "size", "40%"),
+    xmnt_move(*make_tmpfs("jai-tmp", "gid", "0", "mode", "0755", "size", "40%",
+                          "huge", "within_size"),
               *tmp);
   }
-  return ensure_dir(run_jai_user(), "tmp" / sandbox_name_, 01777, kNoFollow);
+  return xopenat(run_jai_user(), "tmp", O_RDONLY | O_NOFOLLOW);
 }
 
 Fd
-Config::make_private_run()
+Config::make_private_tmp(path subdir, bool userowned)
 {
-  ensure_dir(run_jai_user(), "tmp/.run", 0755, kNoFollow);
-  Fd fd = ensure_dir(run_jai_user(), "tmp/.run" / sandbox_name_, 0700,
-                     kNoFollow, true);
-  if (xfstat(*fd).st_uid != user_cred_.uid_ &&
-      fchown(*fd, user_cred_.uid_, user_cred_.gid_))
-    syserr("{}: fchown", fdpath(*fd));
-  return fd;
+  Fd fd = make_private_tmproot();
+  if (!subdir.empty()) {
+    assert(subdir.is_relative());
+    fd = ensure_dir(*fd, subdir, 0755, kNoFollow, false);
+  }
+
+  if (userowned) {
+    fd = ensure_dir(*fd, sandbox_name_, 0700, kNoFollow, true, [this](int fd) {
+      if (fchown(fd, user_cred_.uid_, user_cred_.gid_))
+        syserr("{}: fchown", fdpath(fd));
+    });
+    check_user(*fd);
+    return fd;
+  }
+  else
+    return ensure_dir(*fd, sandbox_name_, 01777, kNoFollow);
 }
 
 Fd
@@ -443,15 +453,12 @@ Config::make_mnt_ns()
   if (mode_ == kStrict)
     passwd = clone_tree(*make_private_passwd());
   path xdgrun = std::format("/run/user/{}", user_cred_.uid_);
-  Fd rundir;
-  if (!grant_directories_.contains(xdgrun)) {
-    if (struct stat sb; !stat(xdgrun.c_str(), &sb)) {
-      check_user(sb, xdgrun);
-      rundir = clone_tree(*make_private_run());
-    }
-    else if (errno != ENOENT)
-      syserr("{}", xdgrun.string());
-  }
+  Fd rundir = grant_directories_.contains(xdgrun)
+                  ? Fd{}
+                  : clone_tree(*make_private_tmp(".run", true));
+  Fd shmdir;
+  if (struct stat sb; !stat("/dev/shm", &sb) && S_ISDIR(sb.st_mode))
+    shmdir = clone_tree(*make_private_tmp(".shm"));
 
   Fd home;
   Fd mapns;
@@ -467,12 +474,9 @@ Config::make_mnt_ns()
     }
     home = clone_tree(*ensure_udir(storage(), cat(sandbox_name_, ".home")));
   }
-  xmnt_setattr(*tmp, attr);
-  xmnt_setattr(*home, attr);
-  if (passwd)
-    xmnt_setattr(*passwd, attr);
-  if (rundir)
-    xmnt_setattr(*rundir, attr);
+  for (int dfd : {*tmp, *home, *passwd, *rundir, *shmdir})
+    if (dfd != -1)
+      xmnt_setattr(dfd, attr);
 
   if (unshare(CLONE_NEWNS))
     syserr("unshare(CLONE_NEWNS)");
@@ -494,6 +498,10 @@ Config::make_mnt_ns()
     xmnt_move(*passwd, -1, "/etc/passwd");
   if (rundir)
     xmnt_move(*rundir, -1, xdgrun);
+  if (shmdir) {
+    umount2("/dev/shm", MNT_DETACH);
+    xmnt_move(*shmdir, -1, "/dev/shm");
+  }
 
   if (grant_cwd_) {
     if (!grant_directories_.contains(cwd())) {
jai.h
index 9cda1cd..1195ae3 100644
--- a/jai.h
+++ b/jai.h
@@ -154,8 +154,8 @@ struct Config {
 
   Fd make_blacklist(int dfd, path name);
   Fd make_home_overlay();
-  Fd make_private_tmp();
-  Fd make_private_run();
+  Fd make_private_tmproot();
+  Fd make_private_tmp(path subdir = {}, bool userowned = false);
   Fd make_private_passwd();
 
   const char *env_lookup(std::string_view var) {