Merge remote-tracking branch 'aosp/upstream-main' into 'aosp/main' am: bbaabc5ab3

Original change: https://android-review.googlesource.com/c/platform/external/sandboxed-api/+/2951214

Change-Id: I2a7d93bce8fce583c9916a3b11c366ddfdda3234
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.github/workflows/fedora-cmake.yml b/.github/workflows/fedora-cmake.yml
index e2a4bb8..9157dd5 100644
--- a/.github/workflows/fedora-cmake.yml
+++ b/.github/workflows/fedora-cmake.yml
@@ -11,7 +11,7 @@
       fail-fast: false
       matrix:
         include:
-          - container: fedora:35
+          - container: fedora:38
             compiler: gcc
             compiler-version: 11  # Only used in cache action so far
             ignore-errors: true  # Stack trace test fails on Fedora (issue #118)
@@ -56,7 +56,7 @@
 
     - name: Create Build Environment
       run: |
-        $RUN_CMD pip3 install --progress-bar=off absl-py clang
+        $RUN_CMD pip3 install --progress-bar=off absl-py 'clang>=13,<14'
         $RUN_CMD cmake -E make_directory $GITHUB_WORKSPACE/build
 
     - name: Configure CMake
diff --git a/.github/workflows/ubuntu-cmake-contrib.yml b/.github/workflows/ubuntu-cmake-contrib.yml
index 193b97d..8d67218 100644
--- a/.github/workflows/ubuntu-cmake-contrib.yml
+++ b/.github/workflows/ubuntu-cmake-contrib.yml
@@ -10,7 +10,7 @@
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-20.04]
+        os: [ubuntu-22.04]
         contrib:
           - brotli
           - c-blosc
@@ -78,7 +78,7 @@
 
     - name: Create Build Environment
       run: |
-        pip3 install absl-py clang
+        pip3 install absl-py 'clang>=14,<15'
         cmake -E make_directory $GITHUB_WORKSPACE/build
 
     - name: Configure CMake
diff --git a/contrib/zopfli/sandboxed.h b/contrib/zopfli/sandboxed.h
index 80442e3..7f25069 100644
--- a/contrib/zopfli/sandboxed.h
+++ b/contrib/zopfli/sandboxed.h
@@ -31,7 +31,7 @@
         .AllowDynamicStartup()
         .AllowWrite()
         .AllowExit()
-        .AllowMmap()
+        .AllowMmapWithoutExec()
         .AllowSystemMalloc()
         .AllowSyscalls({
             __NR_recvmsg,
diff --git a/oss-internship-2020/curl/sandbox.h b/oss-internship-2020/curl/sandbox.h
index 29bbbc2..49cf7ad 100644
--- a/oss-internship-2020/curl/sandbox.h
+++ b/oss-internship-2020/curl/sandbox.h
@@ -38,7 +38,7 @@
         .AllowFutexOp(FUTEX_WAIT_PRIVATE)
         .AllowFutexOp(FUTEX_WAKE_PRIVATE)
         .AllowFutexOp(FUTEX_REQUEUE_PRIVATE)
-        .AllowMmap()
+        .AllowMmapWithoutExec()
         .AllowOpen()
         .AllowSafeFcntl()
         .AllowWrite()
diff --git a/oss-internship-2020/libpng/sandboxed.h b/oss-internship-2020/libpng/sandboxed.h
index c811ddc..d55247e 100644
--- a/oss-internship-2020/libpng/sandboxed.h
+++ b/oss-internship-2020/libpng/sandboxed.h
@@ -38,7 +38,7 @@
         .AllowOpen()
         .AllowExit()
         .AllowStat()
-        .AllowMmap()
+        .AllowMmapWithoutExec()
         .AllowSystemMalloc()
         .AllowSyscalls({
             __NR_futex,
diff --git a/oss-internship-2020/libuv/examples/uvcat.cc b/oss-internship-2020/libuv/examples/uvcat.cc
index 7b54f76..cc930fa 100644
--- a/oss-internship-2020/libuv/examples/uvcat.cc
+++ b/oss-internship-2020/libuv/examples/uvcat.cc
@@ -37,7 +37,7 @@
         .AllowFork()
         .AllowFutexOp(FUTEX_WAKE_PRIVATE)
         .AllowFutexOp(FUTEX_WAIT_PRIVATE)
-        .AllowMmap()
+        .AllowMmapWithoutExec()
         .AllowOpen()
         .AllowEpoll()
         .AllowSyscall(__NR_eventfd2)
diff --git a/oss-internship-2020/libuv/tests/test_os.cc b/oss-internship-2020/libuv/tests/test_os.cc
index 1e5da3b..a96a5ce 100644
--- a/oss-internship-2020/libuv/tests/test_os.cc
+++ b/oss-internship-2020/libuv/tests/test_os.cc
@@ -32,7 +32,7 @@
         .AllowExit()
         .AllowFutexOp(FUTEX_WAKE_PRIVATE)
         .AllowGetIDs()
-        .AllowMmap()
+        .AllowMmapWithoutExec()
         .AllowOpen()
         .AllowWrite()
         .AllowSyscalls({__NR_connect, __NR_socket})
diff --git a/sandboxed_api/bazel/sapi.bzl b/sandboxed_api/bazel/sapi.bzl
index 4b79e41..386c32b 100644
--- a/sandboxed_api/bazel/sapi.bzl
+++ b/sandboxed_api/bazel/sapi.bzl
@@ -146,10 +146,10 @@
 
         # Disable warnings in parsed code
         extra_flags.append("--extra-arg=-Wno-everything")
-        extra_flags += ["--extra-arg=-isystem{}".format(d) for d in cpp_toolchain.built_in_include_directories]
         extra_flags += ["--extra-arg=-D{}".format(d) for d in cc_ctx.defines.to_list()]
         extra_flags += ["--extra-arg=-isystem{}".format(i) for i in cc_ctx.system_includes.to_list()]
         extra_flags += ["--extra-arg=-iquote{}".format(i) for i in quote_includes]
+        extra_flags += ["--extra-arg=-isystem{}".format(d) for d in cpp_toolchain.built_in_include_directories]
     else:
         append_all(extra_flags, "-D", cc_ctx.defines.to_list())
         append_all(extra_flags, "-isystem", cc_ctx.system_includes.to_list())
@@ -236,6 +236,7 @@
         name,
         lib,
         lib_name,
+        malloc = "@bazel_tools//tools/cpp:malloc",
         namespace = "",
         api_version = 1,
         embed = True,
@@ -355,6 +356,7 @@
             # The sandboxing client must have access to all
             "-Wl,-E",  # symbols used in the sandboxed library, so these
         ] + exported_funcs,  # must be both referenced, and exported
+        malloc = malloc,
         deps = [
             ":" + name + ".lib",
             "//sandboxed_api:client",
diff --git a/sandboxed_api/rpcchannel.cc b/sandboxed_api/rpcchannel.cc
index 7eeb447..7e76b05 100644
--- a/sandboxed_api/rpcchannel.cc
+++ b/sandboxed_api/rpcchannel.cc
@@ -140,16 +140,6 @@
   // Try the RPC exit sequence. But, the only thing that matters as a success
   // indicator is whether the Comms channel had been closed
   comms_->SendTLV(comms::kMsgExit, 0, nullptr);
-  bool unused;
-  comms_->RecvBool(&unused);
-
-  if (!comms_->IsTerminated()) {
-    LOG(ERROR) << "Comms channel not terminated in Exit()";
-    // TODO(hamacher): Better error code
-    return absl::FailedPreconditionError(
-        "Comms channel not terminated in Exit()");
-  }
-
   return absl::OkStatus();
 }
 
diff --git a/sandboxed_api/sandbox.cc b/sandboxed_api/sandbox.cc
index b134ac9..88b2696 100644
--- a/sandboxed_api/sandbox.cc
+++ b/sandboxed_api/sandbox.cc
@@ -40,6 +40,7 @@
 #include "sandboxed_api/sandbox2/executor.h"
 #include "sandboxed_api/sandbox2/policy.h"
 #include "sandboxed_api/sandbox2/policybuilder.h"
+#include "sandboxed_api/sandbox2/result.h"
 #include "sandboxed_api/sandbox2/sandbox2.h"
 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
 #include "sandboxed_api/util/fileops.h"
@@ -75,6 +76,7 @@
       .AllowGetPIDs()
       .AllowSleep()
       .AllowReadlink()
+      .AllowAccess()
       .AllowSyscalls({
           __NR_recvmsg,
           __NR_sendmsg,
@@ -105,20 +107,31 @@
     return;
   }
 
+  absl::StatusOr<sandbox2::Result> result;
   if (attempt_graceful_exit) {
-    // Gracefully ask it to exit (with 1 second limit) first, then kill it.
-    Exit();
-  } else {
-    // Kill it straight away
-    s2_->Kill();
+    if (absl::Status requested_exit = rpc_channel_->Exit();
+        !requested_exit.ok()) {
+      LOG(WARNING)
+          << "rpc_channel->Exit() failed, calling AwaitResultWithTimeout(1) "
+          << requested_exit;
+    }
+    result = s2_->AwaitResultWithTimeout(absl::Seconds(1));
+    if (!result.ok()) {
+      LOG(WARNING) << "s2_->AwaitResultWithTimeout failed, status: "
+                   << result.status() << " Killing PID: " << pid();
+    }
   }
 
-  const auto& result = AwaitResult();
-  if (result.final_status() == sandbox2::Result::OK &&
-      result.reason_code() == 0) {
-    VLOG(2) << "Sandbox2 finished with: " << result.ToString();
+  if (!attempt_graceful_exit || !result.ok()) {
+    s2_->Kill();
+    result = s2_->AwaitResult();
+  }
+
+  if (result->final_status() == sandbox2::Result::OK &&
+      result->reason_code() == 0) {
+    VLOG(2) << "Sandbox2 finished with: " << result->ToString();
   } else {
-    LOG(WARNING) << "Sandbox2 finished with: " << result.ToString();
+    LOG(WARNING) << "Sandbox2 finished with: " << result->ToString();
   }
 }
 
@@ -127,7 +140,7 @@
                                         : GetDataDependencyFilePath(lib_path);
 }
 
-absl::Status Sandbox::Init() {
+absl::Status Sandbox::Init(bool use_unotify_monitor) {
   // It's already initialized
   if (is_active()) {
     return absl::OkStatus();
@@ -176,6 +189,9 @@
 
     sandbox2::PolicyBuilder policy_builder;
     InitDefaultPolicyBuilder(&policy_builder);
+  if (use_unotify_monitor) {
+    policy_builder.CollectStacktracesOnSignal(false);
+  }
   auto s2p = ModifyPolicy(&policy_builder);
 
   // Spawn new process from the forkserver.
@@ -196,6 +212,9 @@
 
   s2_ = std::make_unique<sandbox2::Sandbox2>(std::move(executor),
                                              std::move(s2p), CreateNotifier());
+  if (use_unotify_monitor) {
+    SAPI_RETURN_IF_ERROR(s2_->EnableUnotifyMonitor());
+  }
   s2_awaited_ = false;
   auto res = s2_->RunAsync();
 
@@ -306,6 +325,14 @@
   // Copy all arguments into rfcall.
   int i = 0;
   for (auto* arg : args) {
+    if (arg == nullptr) {
+      rfcall.arg_type[i] = v::Type::kPointer;
+      rfcall.arg_size[i] = sizeof(void*);
+      rfcall.args[i].arg_int = 0;
+      VLOG(1) << "CALL ARG: (" << i << "): nullptr";
+      ++i;
+      continue;
+    }
     rfcall.arg_size[i] = arg->GetSize();
     rfcall.arg_type[i] = arg->GetType();
 
@@ -339,7 +366,6 @@
       }
       rfcall.args[i].arg_int = fd->GetRemoteFd();
     }
-
     VLOG(1) << "CALL ARG: (" << i << "), Type: " << arg->GetTypeString()
             << ", Size: " << arg->GetSize() << ", Val: " << arg->ToString();
     ++i;
@@ -364,7 +390,9 @@
 
   // Synchronize all pointers after the call if it's needed.
   for (auto* arg : args) {
-    SAPI_RETURN_IF_ERROR(SynchronizePtrAfter(arg));
+    if (arg != nullptr) {
+      SAPI_RETURN_IF_ERROR(SynchronizePtrAfter(arg));
+    }
   }
 
   VLOG(1) << "CALL EXIT: Type: " << ret->GetTypeString()
diff --git a/sandboxed_api/sandbox.h b/sandboxed_api/sandbox.h
index 577144e..b691e08 100644
--- a/sandboxed_api/sandbox.h
+++ b/sandboxed_api/sandbox.h
@@ -52,7 +52,7 @@
   virtual ~Sandbox();
 
   // Initializes a new sandboxing session.
-  absl::Status Init();
+  absl::Status Init(bool use_unotify_monitor = false);
 
   // Returns whether the current sandboxing session is active.
   bool is_active() const;
diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel
index 9e0ade3..3b08b55 100644
--- a/sandboxed_api/sandbox2/BUILD.bazel
+++ b/sandboxed_api/sandbox2/BUILD.bazel
@@ -41,6 +41,23 @@
 )
 
 cc_library(
+    name = "trace_all_syscalls",
+    hdrs = ["trace_all_syscalls.h"],
+    copts = sapi_platform_copts(),
+    visibility = [
+        "//sandboxed_api/sandbox2:__pkg__",
+    ],
+)
+
+cc_library(
+    name = "testonly_trace_all_syscalls",
+    testonly = True,
+    hdrs = ["trace_all_syscalls.h"],
+    copts = sapi_platform_copts(),
+    visibility = ["//visibility:public"],
+)
+
+cc_library(
     name = "allow_unrestricted_networking",
     hdrs = ["allow_unrestricted_networking.h"],
     copts = sapi_platform_copts(),
@@ -109,15 +126,18 @@
     srcs = [
         "syscall.cc",
         "syscall_defs.cc",
+    ],
+    hdrs = [
+        "syscall.h",
         "syscall_defs.h",
     ],
-    hdrs = ["syscall.h"],
     copts = sapi_platform_copts(),
     visibility = ["//visibility:public"],
     deps = [
         ":util",
         "//sandboxed_api:config",
         "@com_google_absl//absl/algorithm:container",
+        "@com_google_absl//absl/status",
         "@com_google_absl//absl/status:statusor",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/strings:str_format",
@@ -237,7 +257,6 @@
         ":result",
         ":syscall",
         ":util",
-        "//sandboxed_api:config",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/log",
     ],
@@ -529,6 +548,7 @@
         ":stack_trace",
         ":syscall",
         ":util",
+        "//sandboxed_api/sandbox2/network_proxy:client",
         "//sandboxed_api/sandbox2/network_proxy:server",
         "//sandboxed_api/util:file_helpers",
         "//sandboxed_api/util:raw_logging",
@@ -560,6 +580,7 @@
         ":namespace",
         ":policy",
         ":syscall",
+        ":trace_all_syscalls",
         ":violation_cc_proto",
         "//sandboxed_api:config",
         "//sandboxed_api/sandbox2/network_proxy:filtering",
@@ -921,6 +942,7 @@
     deps = [
         ":comms",
         ":sandbox2",
+        ":trace_all_syscalls",
         "//sandboxed_api:testing",
         "@com_google_absl//absl/log",
         "@com_google_absl//absl/strings",
@@ -957,6 +979,7 @@
     copts = sapi_platform_copts(),
     data = [
         "//sandboxed_api/sandbox2/testcases:abort",
+        "//sandboxed_api/sandbox2/testcases:custom_fork",
         "//sandboxed_api/sandbox2/testcases:minimal",
         "//sandboxed_api/sandbox2/testcases:sleep",
         "//sandboxed_api/sandbox2/testcases:starve",
@@ -967,6 +990,7 @@
         "no_qemu_user_mode",
     ],
     deps = [
+        ":fork_client",
         ":sandbox2",
         "//sandboxed_api:config",
         "//sandboxed_api:testing",
diff --git a/sandboxed_api/sandbox2/CMakeLists.txt b/sandboxed_api/sandbox2/CMakeLists.txt
index 116f609..b01c4d9 100644
--- a/sandboxed_api/sandbox2/CMakeLists.txt
+++ b/sandboxed_api/sandbox2/CMakeLists.txt
@@ -26,6 +26,15 @@
   sapi::base
 )
 
+# sandboxed_api/sandbox2:trace_all_syscalls
+add_library(sandbox2_trace_all_syscalls ${SAPI_LIB_TYPE}
+  trace_all_syscalls.h
+)
+add_library(sandbox2::trace_all_syscalls ALIAS sandbox2_trace_all_syscalls)
+target_link_libraries(sandbox2_trace_all_syscalls PRIVATE
+  sapi::base
+)
+
 # sandboxed_api/sandbox2:allow_unrestricted_networking
 add_library(sandbox2_allow_unrestricted_networking ${SAPI_LIB_TYPE}
   allow_unrestricted_networking.h
@@ -194,7 +203,6 @@
          sandbox2::syscall
          sandbox2::util
   PRIVATE sapi::base
-          sapi::config
 )
 
 # sandboxed_api/sandbox2:limits
@@ -422,6 +430,7 @@
           sandbox2::executor
           sandbox2::fork_client
           sandbox2::ipc
+          sandbox2::network_proxy_client
           sandbox2::network_proxy_server
           sandbox2::notify
           sandbox2::policy
@@ -994,6 +1003,7 @@
     sandbox2::comms
     sandbox2::regs
     sandbox2::sandbox2
+    sandbox2::trace_all_syscalls
     sapi::testing
     sapi::test_main
   )
@@ -1039,6 +1049,7 @@
   )
   add_dependencies(sandbox2_sandbox2_test
     sandbox2::testcase_abort
+    sandbox2::testcase_custom_fork
     sandbox2::testcase_minimal
     sandbox2::testcase_sleep
     sandbox2::testcase_tsync
@@ -1050,6 +1061,7 @@
     absl::synchronization
     absl::time
     sapi::config
+    sandbox2::fork_client
     sandbox2::sandbox2
     sapi::testing
     sapi::status_matchers
diff --git a/sandboxed_api/sandbox2/examples/network/network_sandbox.cc b/sandboxed_api/sandbox2/examples/network/network_sandbox.cc
index 684cbde..7dca5f6 100644
--- a/sandboxed_api/sandbox2/examples/network/network_sandbox.cc
+++ b/sandboxed_api/sandbox2/examples/network/network_sandbox.cc
@@ -49,7 +49,7 @@
 std::unique_ptr<sandbox2::Policy> GetPolicy(absl::string_view sandboxee_path) {
   return sandbox2::PolicyBuilder()
       .AllowExit()
-      .AllowMmap()
+      .AllowMmapWithoutExec()
       .AllowRead()
       .AllowWrite()
       .AllowSyscall(__NR_close)
diff --git a/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc b/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc
index da1c800..76208d1 100644
--- a/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc
+++ b/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc
@@ -41,7 +41,7 @@
 std::unique_ptr<sandbox2::Policy> GetPolicy(absl::string_view sandboxee_path) {
   sandbox2::PolicyBuilder builder;
   builder.AllowExit()
-      .AllowMmap()
+      .AllowMmapWithoutExec()
       .AllowRead()
       .AllowWrite()
       .AllowStat()  // printf, puts
@@ -50,6 +50,7 @@
       .AllowSyscall(__NR_lseek)
       .AllowSyscall(__NR_munmap)
       .AllowSyscall(__NR_getpid)
+      .AllowSyscall(__NR_sigaltstack)
       .AllowTcMalloc()
       .AddLibrariesForBinary(sandboxee_path);
   if (absl::GetFlag(FLAGS_connect_with_handler)) {
diff --git a/sandboxed_api/sandbox2/fork_client.cc b/sandboxed_api/sandbox2/fork_client.cc
index 285344e..7ccc22f 100644
--- a/sandboxed_api/sandbox2/fork_client.cc
+++ b/sandboxed_api/sandbox2/fork_client.cc
@@ -27,6 +27,13 @@
 
 using ::sapi::file_util::fileops::FDCloser;
 
+ForkClient::ForkClient(pid_t pid, Comms* comms, bool is_global)
+    : pid_(pid), comms_(comms), is_global_(is_global) {
+}
+
+ForkClient::~ForkClient() {
+}
+
 SandboxeeProcess ForkClient::SendRequest(const ForkRequest& request,
                                          int exec_fd, int comms_fd) {
   SandboxeeProcess process;
diff --git a/sandboxed_api/sandbox2/fork_client.h b/sandboxed_api/sandbox2/fork_client.h
index 842b152..60d483e 100644
--- a/sandboxed_api/sandbox2/fork_client.h
+++ b/sandboxed_api/sandbox2/fork_client.h
@@ -37,9 +37,10 @@
 
 class ForkClient {
  public:
-  ForkClient(pid_t pid, Comms* comms) : pid_(pid), comms_(comms) {}
+  ForkClient(pid_t pid, Comms* comms) : ForkClient(pid, comms, false) {}
   ForkClient(const ForkClient&) = delete;
   ForkClient& operator=(const ForkClient&) = delete;
+  ~ForkClient();
 
   // Sends the fork request over the supplied Comms channel.
   SandboxeeProcess SendRequest(const ForkRequest& request, int exec_fd,
@@ -48,10 +49,16 @@
   pid_t pid() { return pid_; }
 
  private:
+  friend class GlobalForkClient;
+
+  ForkClient(pid_t pid, Comms* comms, bool is_global);
+
   // Pid of the ForkServer.
   pid_t pid_;
   // Comms channel connecting with the ForkServer. Not owned by the object.
   Comms* comms_ ABSL_GUARDED_BY(comms_mutex_);
+  // Is it the global forkserver
+  bool is_global_;
   // Mutex locking transactions (requests) over the Comms channel.
   absl::Mutex comms_mutex_;
 };
diff --git a/sandboxed_api/sandbox2/forkserver.cc b/sandboxed_api/sandbox2/forkserver.cc
index 1106e02..c8b381b 100644
--- a/sandboxed_api/sandbox2/forkserver.cc
+++ b/sandboxed_api/sandbox2/forkserver.cc
@@ -543,16 +543,20 @@
   cap_t wanted_caps = cap_init();  // starts as empty set, ie. no caps
   SAPI_RAW_CHECK(wanted_caps, "failed to cap_init()");
 
-  for (cap_flag_t flag : {CAP_EFFECTIVE, CAP_PERMITTED}) {
-    cap_flag_value_t value;
-    int rc = cap_get_flag(have_caps, CAP_SETFCAP, flag, &value);
-    SAPI_RAW_CHECK(!rc, "cap_get_flag");
-    if (value == CAP_SET) {
-      cap_value_t caps_to_set[1] = {
-          CAP_SETFCAP,
-      };
-      rc = cap_set_flag(wanted_caps, flag, 1, caps_to_set, CAP_SET);
-      SAPI_RAW_CHECK(!rc, "cap_set_flag");
+  // CAP_SYS_PTRACE appears to be needed for apparmor (or possibly yama)
+  // CAP_SETFCAP is needed on newer kernels (5.10 needs it, 4.15 does not)
+  for (cap_value_t cap : {CAP_SYS_PTRACE, CAP_SETFCAP}) {
+    for (cap_flag_t flag : {CAP_EFFECTIVE, CAP_PERMITTED}) {
+      cap_flag_value_t value;
+      int rc = cap_get_flag(have_caps, cap, flag, &value);
+      SAPI_RAW_CHECK(!rc, "cap_get_flag");
+      if (value == CAP_SET) {
+        cap_value_t caps_to_set[1] = {
+            cap,
+        };
+        rc = cap_set_flag(wanted_caps, flag, 1, caps_to_set, CAP_SET);
+        SAPI_RAW_CHECK(!rc, "cap_set_flag");
+      }
     }
   }
 
diff --git a/sandboxed_api/sandbox2/global_forkclient.cc b/sandboxed_api/sandbox2/global_forkclient.cc
index 8a6231e..b014931 100644
--- a/sandboxed_api/sandbox2/global_forkclient.cc
+++ b/sandboxed_api/sandbox2/global_forkclient.cc
@@ -271,7 +271,7 @@
                  "already running");
   absl::StatusOr<std::unique_ptr<GlobalForkClient>> forkserver =
       StartGlobalForkServer();
-  SAPI_RAW_CHECK(forkserver.ok(), forkserver.status().message().data());
+  SAPI_RAW_CHECK(forkserver.ok(), forkserver.status().ToString().c_str());
   instance_ = forkserver->release();
 }
 
diff --git a/sandboxed_api/sandbox2/global_forkclient.h b/sandboxed_api/sandbox2/global_forkclient.h
index 4517c27..13e8ad1 100644
--- a/sandboxed_api/sandbox2/global_forkclient.h
+++ b/sandboxed_api/sandbox2/global_forkclient.h
@@ -43,7 +43,7 @@
 class GlobalForkClient {
  public:
   GlobalForkClient(int fd, pid_t pid)
-      : comms_(fd), fork_client_(pid, &comms_) {}
+      : comms_(fd), fork_client_(pid, &comms_, /*is_global=*/true) {}
 
   static SandboxeeProcess SendRequest(const ForkRequest& request, int exec_fd,
                                       int comms_fd)
diff --git a/sandboxed_api/sandbox2/logserver.proto b/sandboxed_api/sandbox2/logserver.proto
index 0329c24..a2bf74d 100644
--- a/sandboxed_api/sandbox2/logserver.proto
+++ b/sandboxed_api/sandbox2/logserver.proto
@@ -13,12 +13,13 @@
 // limitations under the License.
 
 syntax = "proto3";
+
 package sandbox2;
 
 message LogMessage {
   optional int32 severity = 1;
   optional string path = 2;
   optional int32 line = 3;
-  optional string message = 4;
+  optional bytes message = 4;
   optional int32 pid = 5;
 }
diff --git a/sandboxed_api/sandbox2/monitor_base.cc b/sandboxed_api/sandbox2/monitor_base.cc
index 8423415..76978c9 100644
--- a/sandboxed_api/sandbox2/monitor_base.cc
+++ b/sandboxed_api/sandbox2/monitor_base.cc
@@ -50,7 +50,9 @@
 #include "sandboxed_api/sandbox2/limits.h"
 #include "sandboxed_api/sandbox2/mounts.h"
 #include "sandboxed_api/sandbox2/namespace.h"
+#include "sandboxed_api/sandbox2/network_proxy/client.h"
 #include "sandboxed_api/sandbox2/network_proxy/server.h"
+#include "sandboxed_api/sandbox2/notify.h"
 #include "sandboxed_api/sandbox2/policy.h"
 #include "sandboxed_api/sandbox2/result.h"
 #include "sandboxed_api/sandbox2/stack_trace.h"
diff --git a/sandboxed_api/sandbox2/mounts.cc b/sandboxed_api/sandbox2/mounts.cc
index 097d53d..227d207 100644
--- a/sandboxed_api/sandbox2/mounts.cc
+++ b/sandboxed_api/sandbox2/mounts.cc
@@ -163,7 +163,7 @@
   if (n1.node_case() != n2.node_case()) {
     return false;
   }
-  // Compare proto fileds
+  // Compare proto fields
   switch (n1.node_case()) {
     case MountTree::Node::kFileNode:
       // Check whether files are the same (e.g. symlinks / hardlinks)
@@ -185,7 +185,7 @@
     return false;
   }
 
-  // Compare proto fileds
+  // Compare proto fields
   switch (n1.node_case()) {
     case MountTree::Node::kFileNode:
       return n1.file_node().writable() == n2.file_node().writable();
@@ -309,7 +309,7 @@
       if (!internal::IsWritable(curtree->node()) &&
           internal::IsWritable(new_node)) {
         SAPI_RAW_LOG(INFO,
-                     "Chaning %s to writable, was insterted read-only before",
+                     "Changing %s to writable, was inserted read-only before",
                      std::string(path).c_str());
         *curtree->mutable_node() = new_node;
         return absl::OkStatus();
@@ -317,7 +317,7 @@
       if (internal::IsWritable(curtree->node()) &&
           !internal::IsWritable(new_node)) {
         SAPI_RAW_LOG(INFO,
-                     "Inserting %s read-only is a nop, as it was insterted "
+                     "Inserting %s read-only is a nop, as it was inserted "
                      "writable before",
                      std::string(path).c_str());
         return absl::OkStatus();
@@ -349,7 +349,7 @@
 absl::Status Mounts::AddDirectoryAt(absl::string_view outside,
                                     absl::string_view inside, bool is_ro) {
   MountTree::Node node;
-  auto dir_node = node.mutable_dir_node();
+  auto* dir_node = node.mutable_dir_node();
   dir_node->set_outside(std::string(outside));
   dir_node->set_writable(!is_ro);
   return Insert(inside, node);
diff --git a/sandboxed_api/sandbox2/notify.h b/sandboxed_api/sandbox2/notify.h
index 8d00a7f..0195428 100644
--- a/sandboxed_api/sandbox2/notify.h
+++ b/sandboxed_api/sandbox2/notify.h
@@ -21,7 +21,6 @@
 
 #include "absl/base/attributes.h"
 #include "absl/log/log.h"
-#include "sandboxed_api/config.h"
 #include "sandboxed_api/sandbox2/comms.h"
 #include "sandboxed_api/sandbox2/result.h"
 #include "sandboxed_api/sandbox2/syscall.h"
diff --git a/sandboxed_api/sandbox2/notify_test.cc b/sandboxed_api/sandbox2/notify_test.cc
index ad77dc8..6942f50 100644
--- a/sandboxed_api/sandbox2/notify_test.cc
+++ b/sandboxed_api/sandbox2/notify_test.cc
@@ -33,6 +33,7 @@
 #include "sandboxed_api/sandbox2/policybuilder.h"
 #include "sandboxed_api/sandbox2/sandbox2.h"
 #include "sandboxed_api/sandbox2/syscall.h"
+#include "sandboxed_api/sandbox2/trace_all_syscalls.h"
 #include "sandboxed_api/testing.h"
 
 namespace sandbox2 {
@@ -126,5 +127,21 @@
   EXPECT_THAT(result.reason_code(), Eq(33));
 }
 
+// Test EventSyscallTrap on personality syscall through TraceAllSyscalls
+TEST(NotifyTest, TraceAllAllowPersonality) {
+  const std::string path = GetTestSourcePath("sandbox2/testcases/personality");
+  std::vector<std::string> args = {path};
+  auto policy = CreateDefaultPermissiveTestPolicy(path)
+                    .DefaultAction(TraceAllSyscalls())
+                    .BuildOrDie();
+  Sandbox2 s2(std::make_unique<Executor>(path, args),
+              NotifyTestcasePolicy(path),
+              std::make_unique<PersonalityNotify>(/*allow=*/true));
+  auto result = s2.Run();
+
+  ASSERT_THAT(result.final_status(), Eq(Result::OK));
+  EXPECT_THAT(result.reason_code(), Eq(22));
+}
+
 }  // namespace
 }  // namespace sandbox2
diff --git a/sandboxed_api/sandbox2/policy.cc b/sandboxed_api/sandbox2/policy.cc
index 6a4b2f4..721abcf 100644
--- a/sandboxed_api/sandbox2/policy.cc
+++ b/sandboxed_api/sandbox2/policy.cc
@@ -183,8 +183,9 @@
   policy.insert(policy.end(),
                 {
 #ifdef __NR_clone3
-                    // Disallow clone3
-                    JEQ32(__NR_clone3, DENY),
+                    // Disallow clone3. Errno instead of DENY so that libraries
+                    // can fallback to regular clone/clone2.
+                    JEQ32(__NR_clone3, ERRNO(ENOSYS)),
 #endif
                     // Disallow clone3 and clone with unsafe flags.  This uses
                     // LOAD_SYSCALL_NR from above.
diff --git a/sandboxed_api/sandbox2/policybuilder.cc b/sandboxed_api/sandbox2/policybuilder.cc
index 80b42d8..6fbda54 100644
--- a/sandboxed_api/sandbox2/policybuilder.cc
+++ b/sandboxed_api/sandbox2/policybuilder.cc
@@ -60,6 +60,7 @@
 #include "sandboxed_api/sandbox2/namespace.h"
 #include "sandboxed_api/sandbox2/policy.h"
 #include "sandboxed_api/sandbox2/syscall.h"
+#include "sandboxed_api/sandbox2/trace_all_syscalls.h"
 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
 #include "sandboxed_api/sandbox2/violation.pb.h"
 #include "sandboxed_api/util/path.h"
@@ -70,6 +71,9 @@
 #include <asm/termbits.h>  // On PPC, TCGETS macro needs termios
 #endif
 
+#ifndef MAP_FIXED_NOREPLACE
+#define MAP_FIXED_NOREPLACE 0x100000
+#endif
 #ifndef PR_SET_VMA
 #define PR_SET_VMA 0x53564d41
 #endif
@@ -285,6 +289,7 @@
         LABEL(&labels, prot_none),
         ARG_32(3),  // flags
         JEQ32(MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, ALLOW),
+        JEQ32(MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, ALLOW),
         JEQ32(MAP_ANONYMOUS | MAP_PRIVATE, ALLOW),
 
         LABEL(&labels, mmap_end),
@@ -329,7 +334,7 @@
   // example:
   // https://github.com/llvm/llvm-project/blob/596d534ac3524052df210be8d3c01a33b2260a42/compiler-rt/lib/asan/asan_allocator.cpp#L980
   // https://github.com/llvm/llvm-project/blob/62ec4ac90738a5f2d209ed28c822223e58aaaeb7/compiler-rt/lib/sanitizer_common/sanitizer_allocator_secondary.h#L98
-  AllowMmap();
+  AllowMmapWithoutExec();
   AllowSyscall(__NR_munmap);
   AllowSyscall(__NR_sched_yield);
 
@@ -359,7 +364,7 @@
   OverridableBlockSyscallWithErrno(__NR_ioctl, EPERM);
   // https://github.com/llvm/llvm-project/blob/9aa39481d9eb718e872993791547053a3c1f16d5/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp#L150
   // https://sourceware.org/git/?p=glibc.git;a=blob;f=nptl/pthread_getattr_np.c;h=de7edfa0928224eb8375e2fe894d6677570fbb3b;hb=HEAD#l188
-  OverridableBlockSyscallWithErrno(__NR_sched_getaffinity, EPERM);
+  AllowSyscall(__NR_sched_getaffinity);
   // https://github.com/llvm/llvm-project/blob/02c2b472b510ff55679844c087b66e7837e13dc2/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp#L434
 #ifdef __NR_readlink
   OverridableBlockSyscallWithErrno(__NR_readlink, ENOENT);
@@ -415,6 +420,14 @@
                                           });
 }
 
+PolicyBuilder& PolicyBuilder::AllowMmapWithoutExec() {
+  return AddPolicyOnMmap({
+      ARG_32(2),
+      BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, PROT_EXEC, 1, 0),
+      ALLOW,
+  });
+}
+
 PolicyBuilder& PolicyBuilder::AllowMmap() {
   return AllowSyscalls(kMmapSyscalls);
 }
@@ -760,11 +773,13 @@
   AllowFutexOp(FUTEX_WAKE);
   AllowRead();
   AllowOpen();
+  AllowPoll();
   AllowSyscall(__NR_close);
   AddPolicyOnSyscall(__NR_rt_sigprocmask, {
                                               ARG_32(0),
                                               JEQ32(SIG_SETMASK, ALLOW),
                                           });
+  AllowPrctlSetVma();
   if (cpu_fence_mode == kAllowSlowFences) {
     AllowSyscall(__NR_sched_getaffinity);
     AllowSyscall(__NR_sched_setaffinity);
@@ -796,6 +811,9 @@
 }
 
 PolicyBuilder& PolicyBuilder::AllowGetRlimit() {
+#ifdef __NR_prlimit64
+  AddPolicyOnSyscall(__NR_prlimit64, {ARG(2), JEQ64(0, 0, ALLOW)});
+#endif
   return AllowSyscalls({
 #ifdef __NR_getrlimit
       __NR_getrlimit,
@@ -808,6 +826,9 @@
 
 PolicyBuilder& PolicyBuilder::AllowSetRlimit() {
   return AllowSyscalls({
+#ifdef __NR_prlimit64
+      __NR_prlimit64,
+#endif
 #ifdef __NR_setrlimit
       __NR_setrlimit,
 #endif
@@ -856,7 +877,7 @@
                                               ARG_32(0),
                                               JEQ32(SIG_BLOCK, ALLOW),
                                           });
-  AllowSyscall(__NR_prlimit64);
+  AllowGetRlimit();
 
   // For LOG(FATAL)
   return AddPolicyOnSyscall(__NR_kill,
@@ -990,14 +1011,14 @@
   OverridableBlockSyscallWithErrno(__NR_readlink, ENOENT);
 #endif
 
-#ifdef __NR_prlimit64
-  OverridableBlockSyscallWithErrno(__NR_prlimit64, EPERM);
-#endif
+  AllowGetRlimit();
   AddPolicyOnSyscall(__NR_mprotect, {
                                         ARG_32(2),
                                         JEQ32(PROT_READ, ALLOW),
                                     });
 
+  OverridableBlockSyscallWithErrno(__NR_sigaltstack, ENOSYS);
+
   return *this;
 }
 
@@ -1224,6 +1245,11 @@
   return *this;
 }
 
+PolicyBuilder& PolicyBuilder::DefaultAction(TraceAllSyscalls) {
+  default_action_ = SANDBOX2_TRACE;
+  return *this;
+}
+
 absl::StatusOr<std::string> PolicyBuilder::ValidateAbsolutePath(
     absl::string_view path) {
   if (!file::IsAbsolutePath(path)) {
diff --git a/sandboxed_api/sandbox2/policybuilder.h b/sandboxed_api/sandbox2/policybuilder.h
index c832d00..5485833 100644
--- a/sandboxed_api/sandbox2/policybuilder.h
+++ b/sandboxed_api/sandbox2/policybuilder.h
@@ -43,6 +43,7 @@
 namespace sandbox2 {
 
 class AllowAllSyscalls;
+class TraceAllSyscalls;
 class UnrestrictedNetworking;
 
 // PolicyBuilder is a helper class to simplify creation of policies. The builder
@@ -245,20 +246,25 @@
 
   // Appends code to allow mmap. Specifically this allows mmap and mmap2 syscall
   // on architectures where this syscalls exist.
+  // Prefer using AllowMmapWithoutExec as allowing mapping executable pages
+  // makes exploitation easier.
   PolicyBuilder& AllowMmap();
 
+  // Appends code to allow mmap calls that don't specify PROT_EXEC.
+  PolicyBuilder& AllowMmapWithoutExec();
+
   // Appends code to allow calling futex with the given operation.
   PolicyBuilder& AllowFutexOp(int op);
 
   // Appends code to allow opening and possibly creating files or directories.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - creat
   // - open
   // - openat
   PolicyBuilder& AllowOpen();
 
   // Appends code to allow calling stat, fstat and lstat.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - fstat
   // - fstat64
   // - fstatat
@@ -313,7 +319,7 @@
   PolicyBuilder& AllowChown();
 
   // Appends code to the policy to allow reading from file descriptors.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - read
   // - readv
   // - preadv
@@ -321,7 +327,7 @@
   PolicyBuilder& AllowRead();
 
   // Appends code to the policy to allow writing to file descriptors.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - write
   // - writev
   // - pwritev
@@ -329,37 +335,37 @@
   PolicyBuilder& AllowWrite();
 
   // Appends code to allow reading directories.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - getdents
   // - getdents64
   PolicyBuilder& AllowReaddir();
 
   // Appends code to allow reading symbolic links.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - readlink
   // - readlinkat
   PolicyBuilder& AllowReadlink();
 
   // Appends code to allow creating links.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - link
   // - linkat
   PolicyBuilder& AllowLink();
 
   // Appends code to allow creating symbolic links.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - symlink
   // - symlinkat
   PolicyBuilder& AllowSymlink();
 
   // Appends code to allow creating directories.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - mkdir
   // - mkdirat
   PolicyBuilder& AllowMkdir();
 
   // Appends code to allow changing file timestamps.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - futimens
   // - utime
   // - utimensat
@@ -367,7 +373,7 @@
   PolicyBuilder& AllowUtime();
 
   // Appends code to allow safe calls to fcntl.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - fcntl
   // - fcntl64 (on architectures where it exists)
   //
@@ -377,7 +383,7 @@
   PolicyBuilder& AllowSafeFcntl();
 
   // Appends code to allow creating new processes.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - fork
   // - vfork
   // - clone
@@ -388,19 +394,19 @@
   PolicyBuilder& AllowFork();
 
   // Appends code to allow waiting for processes.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - waitpid (on architectures where it exists)
   // - wait4
   PolicyBuilder& AllowWait();
 
   // Appends code to allow setting alarms / interval timers.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - alarm (on architectures where it exists)
   // - setitimer
   PolicyBuilder& AllowAlarm();
 
   // Appends code to allow setting up signal handlers, returning from them, etc.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - rt_sigaction
   // - rt_sigreturn
   // - rt_procmask
@@ -411,12 +417,12 @@
   PolicyBuilder& AllowHandleSignals();
 
   // Appends code to allow doing the TCGETS ioctl.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - ioctl (when the first argument is TCGETS)
   PolicyBuilder& AllowTCGETS();
 
   // Appends code to allow to getting the current time.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - time
   // - gettimeofday
   // - clock_gettime
@@ -450,19 +456,19 @@
   PolicyBuilder& AllowGetPGIDs();
 
   // Appends code to allow getting the rlimits.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - getrlimit
   // - ugetrlimit (on architectures where it exist)
   PolicyBuilder& AllowGetRlimit();
 
   // Appends code to allow setting the rlimits.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - setrlimit
   // - usetrlimit (on architectures where it exist)
   PolicyBuilder& AllowSetRlimit();
 
   // Appends code to allow reading random bytes.
-  // Allows these sycalls:
+  // Allows these syscalls:
   // - getrandom (with no flags or GRND_NONBLOCK)
   //
   PolicyBuilder& AllowGetRandom();
@@ -707,6 +713,12 @@
   // sandbox-team@ first if unsure.
   PolicyBuilder& DefaultAction(AllowAllSyscalls);
 
+  // Changes the default action to SANDBOX2_TRACE.
+  // All syscalls not handled explicitly by the policy will be passed off to
+  // the `sandbox2::Notify` implementation given to the `sandbox2::Sandbox2`
+  // instance.
+  PolicyBuilder& DefaultAction(TraceAllSyscalls);
+
   ABSL_DEPRECATED("Use DefaultAction(sandbox2::AllowAllSyscalls()) instead")
   PolicyBuilder& DangerDefaultAllowAll();
 
diff --git a/sandboxed_api/sandbox2/sandbox2_test.cc b/sandboxed_api/sandbox2/sandbox2_test.cc
index 8060d06..828803f 100644
--- a/sandboxed_api/sandbox2/sandbox2_test.cc
+++ b/sandboxed_api/sandbox2/sandbox2_test.cc
@@ -35,6 +35,7 @@
 #include "absl/time/time.h"
 #include "sandboxed_api/config.h"
 #include "sandboxed_api/sandbox2/executor.h"
+#include "sandboxed_api/sandbox2/fork_client.h"
 #include "sandboxed_api/sandbox2/policy.h"
 #include "sandboxed_api/sandbox2/policybuilder.h"
 #include "sandboxed_api/sandbox2/result.h"
@@ -51,6 +52,7 @@
 using ::testing::IsEmpty;
 using ::testing::IsTrue;
 using ::testing::Lt;
+using ::testing::Ne;
 
 class Sandbox2Test : public ::testing::TestWithParam<bool> {
  public:
@@ -195,6 +197,23 @@
   EXPECT_EQ(result.final_status(), Result::OK);
 }
 
+TEST_P(Sandbox2Test, CustomForkserverWorks) {
+  const std::string path = GetTestSourcePath("sandbox2/testcases/custom_fork");
+  std::vector<std::string> args = {path};
+  auto fork_executor = std::make_unique<Executor>(path, args);
+  std::unique_ptr<ForkClient> fork_client = fork_executor->StartForkServer();
+  ASSERT_THAT(fork_client.get(), Ne(nullptr));
+
+  SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
+                            CreateDefaultTestPolicy(path).TryBuild());
+
+  Sandbox2 sandbox(std::make_unique<Executor>(fork_client.get()),
+                   std::move(policy));
+  ASSERT_THAT(SetUpSandbox(&sandbox), IsOk());
+  Result result = sandbox.Run();
+  EXPECT_EQ(result.final_status(), Result::OK);
+}
+
 TEST(StarvationTest, MonitorIsNotStarvedByTheSandboxee) {
   const std::string path = GetTestSourcePath("sandbox2/testcases/starve");
 
diff --git a/sandboxed_api/sandbox2/stack_trace.cc b/sandboxed_api/sandbox2/stack_trace.cc
index 2b970ad..11f9b6f 100644
--- a/sandboxed_api/sandbox2/stack_trace.cc
+++ b/sandboxed_api/sandbox2/stack_trace.cc
@@ -145,7 +145,7 @@
       .AllowSyscall(__NR_recvmsg)
 
       // libunwind
-      .AllowMmap()
+      .AllowMmapWithoutExec()
       .AllowStat()
       .AllowSyscall(__NR_lseek)
 #ifdef __NR__llseek
diff --git a/sandboxed_api/sandbox2/syscall_defs.cc b/sandboxed_api/sandbox2/syscall_defs.cc
index ffbaa36..a7b41ed 100644
--- a/sandboxed_api/sandbox2/syscall_defs.cc
+++ b/sandboxed_api/sandbox2/syscall_defs.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "absl/algorithm/container.h"
+#include "absl/status/status.h"
 #include "absl/status/statusor.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/str_cat.h"
@@ -18,56 +19,15 @@
 
 namespace sandbox2 {
 
-// Type of a given syscall argument. Used with argument conversion routines.
-enum ArgType {
-  kGen = 1,
-  kInt,
-  kPath,
-  kHex,
-  kOct,
-  kSocketCall,
-  kSocketCallPtr,
-  kSignal,
-  kString,
-  kAddressFamily,
-  kSockaddr,
-  kSockmsghdr,
-  kCloneFlag,
-};
-
-// Single syscall definition
-struct SyscallTable::Entry {
-  // Returns the number of arguments which given syscall takes.
-  int GetNumArgs() const {
-    if (num_args < 0 || num_args > syscalls::kMaxArgs) {
-      return syscalls::kMaxArgs;
-    }
-    return num_args;
-  }
-
-  static std::string GetArgumentDescription(uint64_t value, ArgType type,
-                                            pid_t pid);
-
-  static constexpr bool BySyscallNr(const SyscallTable::Entry& a,
-                                    const SyscallTable::Entry& b) {
-    return a.nr < b.nr;
-  }
-
-  int nr;
-  absl::string_view name;
-  int num_args;
-  std::array<ArgType, syscalls::kMaxArgs> arg_types;
-};
-
 std::string SyscallTable::Entry::GetArgumentDescription(uint64_t value,
-                                                        ArgType type,
+                                                        syscalls::ArgType type,
                                                         pid_t pid) {
   std::string ret = absl::StrFormat("%#x", value);
   switch (type) {
-    case kOct:
+    case syscalls::kOct:
       absl::StrAppendFormat(&ret, " [\\0%o]", value);
       break;
-    case kPath:
+    case syscalls::kPath:
       if (auto path_or = util::ReadCPathFromPid(pid, value); path_or.ok()) {
         absl::StrAppendFormat(&ret, " ['%s']",
                               absl::CHexEscape(path_or.value()));
@@ -75,7 +35,7 @@
         absl::StrAppend(&ret, " [unreadable path]");
       }
       break;
-    case kInt:
+    case syscalls::kInt:
       absl::StrAppendFormat(&ret, " [%d]", value);
       break;
     default:
@@ -85,14 +45,11 @@
 }
 
 absl::string_view SyscallTable::GetName(int syscall) const {
-  auto it = absl::c_lower_bound(
-      data_, syscall, [](const SyscallTable::Entry& entry, int syscall) {
-        return entry.nr < syscall;
-      });
-  if (it == data_.end() || it->nr != syscall) {
+  auto entry = GetEntry(syscall);
+  if (!entry.ok()) {
     return "";
   }
-  return it->name;
+  return entry->name;
 }
 
 namespace {
@@ -108,32 +65,61 @@
 struct UnknownArguments {};
 constexpr SyscallTable::Entry MakeEntry(int nr, absl::string_view name,
                                         UnknownArguments) {
-  return {nr, name, -1, {kGen, kGen, kGen, kGen, kGen, kGen}};
+  return {nr,
+          name,
+          -1,
+          {syscalls::kGen, syscalls::kGen, syscalls::kGen, syscalls::kGen,
+           syscalls::kGen, syscalls::kGen}};
 }
 
 }  // namespace
 
+absl::StatusOr<SyscallTable::Entry> SyscallTable::GetEntry(int syscall) const {
+  auto it = absl::c_lower_bound(
+      data_, syscall, [](const SyscallTable::Entry& entry, int syscall) {
+        return entry.nr < syscall;
+      });
+  if (it == data_.end() || it->nr != syscall) {
+    return absl::NotFoundError(absl::StrCat("Syscall not found: ", syscall));
+  }
+  return *it;
+}
+
+absl::StatusOr<SyscallTable::Entry> SyscallTable::GetEntry(
+    absl::string_view name) const {
+  // Note: There's no uniqueness guarantee of syscall names in the table, but
+  // other than typos it's likely safe to assume uniqueness.
+  auto filter = [name](const SyscallTable::Entry& entry) {
+    return entry.name == name;
+  };
+  auto it = absl::c_find_if(data_, filter);
+  if (it != data_.end()) {
+    return *it;
+  } else {
+    return absl::NotFoundError(absl::StrCat("Name not found: ", name));
+  }
+}
+
 std::vector<std::string> SyscallTable::GetArgumentsDescription(
     int syscall, const uint64_t values[], pid_t pid) const {
   static SyscallTable::Entry kInvalidEntry =
       MakeEntry(-1, "", UnknownArguments());
-  auto it = absl::c_lower_bound(
-      data_, syscall, [](const SyscallTable::Entry& entry, int syscall) {
-        return entry.nr < syscall;
-      });
-  const auto& entry =
-      it != data_.end() && it->nr == syscall ? *it : kInvalidEntry;
+  auto entry = GetEntry(syscall);
+  if (!entry.ok()) {
+    entry = kInvalidEntry;
+  }
 
-  int num_args = entry.GetNumArgs();
+  int num_args = entry->GetNumArgs();
   std::vector<std::string> rv;
   rv.reserve(num_args);
   for (int i = 0; i < num_args; ++i) {
     rv.push_back(SyscallTable::Entry::GetArgumentDescription(
-        values[i], entry.arg_types[i], pid));
+        values[i], entry->arg_types[i], pid));
   }
   return rv;
 }
 
+namespace syscalls {
 namespace {
 
 // TODO(C++20) Use std::is_sorted
@@ -494,6 +480,33 @@
     MakeEntry(332, "statx", kInt, kPath, kHex, kHex, kHex),
     MakeEntry(333, "io_pgetevents", UnknownArguments()),
     MakeEntry(334, "rseq", kHex, kInt, kHex, kHex),
+    MakeEntry(435, "clone3", kHex, kInt),
+    MakeEntry(436, "close_range", kInt, kInt, kHex),
+    MakeEntry(437, "openat2", kInt, kPath, kHex, kInt),
+    MakeEntry(438, "pidfd_getfd", UnknownArguments()),
+    MakeEntry(439, "faccessat2", kInt, kPath, kHex, kHex),
+    MakeEntry(440, "process_madvise", UnknownArguments()),
+    MakeEntry(441, "epoll_pwait2", UnknownArguments()),
+    MakeEntry(442, "mount_setattr", UnknownArguments()),
+    MakeEntry(443, "quotactl_fd", UnknownArguments()),
+    MakeEntry(444, "landlock_create_ruleset", UnknownArguments()),
+    MakeEntry(445, "landlock_add_rule", UnknownArguments()),
+    MakeEntry(446, "landlock_restrict_self", UnknownArguments()),
+    MakeEntry(447, "memfd_secret", UnknownArguments()),
+    MakeEntry(448, "process_mrelease", UnknownArguments()),
+    MakeEntry(449, "futex_waitv", UnknownArguments()),
+    MakeEntry(450, "set_mempolicy_home_node", UnknownArguments()),
+    MakeEntry(451, "cachestat", UnknownArguments()),
+    MakeEntry(452, "fchmodat2", kInt, kPath, kHex, kHex),
+    MakeEntry(453, "map_shadow_stack", UnknownArguments()),
+    MakeEntry(454, "futex_wake", UnknownArguments()),
+    MakeEntry(455, "futex_wait", UnknownArguments()),
+    MakeEntry(456, "futex_requeue", UnknownArguments()),
+    MakeEntry(457, "statmount", UnknownArguments()),
+    MakeEntry(458, "listmount", UnknownArguments()),
+    MakeEntry(459, "lsm_get_self_attr", UnknownArguments()),
+    MakeEntry(460, "lsm_set_self_attr", UnknownArguments()),
+    MakeEntry(461, "lsm_list_modules", UnknownArguments()),
     // clang-format on
 };
 
@@ -860,6 +873,33 @@
     MakeEntry(355, "getrandom", kGen, kInt, kHex),
     MakeEntry(356, "memfd_create", kString, kHex),
     MakeEntry(357, "bpf", kInt, kHex, kInt),
+    MakeEntry(435, "clone3", kHex, kInt),
+    MakeEntry(436, "close_range", kInt, kInt, kHex),
+    MakeEntry(437, "openat2", kInt, kPath, kHex, kInt),
+    MakeEntry(438, "pidfd_getfd", UnknownArguments()),
+    MakeEntry(439, "faccessat2", kInt, kPath, kHex, kHex),
+    MakeEntry(440, "process_madvise", UnknownArguments()),
+    MakeEntry(441, "epoll_pwait2", UnknownArguments()),
+    MakeEntry(442, "mount_setattr", UnknownArguments()),
+    MakeEntry(443, "quotactl_fd", UnknownArguments()),
+    MakeEntry(444, "landlock_create_ruleset", UnknownArguments()),
+    MakeEntry(445, "landlock_add_rule", UnknownArguments()),
+    MakeEntry(446, "landlock_restrict_self", UnknownArguments()),
+    MakeEntry(447, "memfd_secret", UnknownArguments()),
+    MakeEntry(448, "process_mrelease", UnknownArguments()),
+    MakeEntry(449, "futex_waitv", UnknownArguments()),
+    MakeEntry(450, "set_mempolicy_home_node", UnknownArguments()),
+    MakeEntry(451, "cachestat", UnknownArguments()),
+    MakeEntry(452, "fchmodat2", kInt, kPath, kHex, kHex),
+    MakeEntry(453, "map_shadow_stack", UnknownArguments()),
+    MakeEntry(454, "futex_wake", UnknownArguments()),
+    MakeEntry(455, "futex_wait", UnknownArguments()),
+    MakeEntry(456, "futex_requeue", UnknownArguments()),
+    MakeEntry(457, "statmount", UnknownArguments()),
+    MakeEntry(458, "listmount", UnknownArguments()),
+    MakeEntry(459, "lsm_get_self_attr", UnknownArguments()),
+    MakeEntry(460, "lsm_set_self_attr", UnknownArguments()),
+    MakeEntry(461, "lsm_list_modules", UnknownArguments()),
     // clang-format on
 };
 
@@ -1239,6 +1279,32 @@
     MakeEntry(379, "copy_file_range", kInt, kHex, kInt, kHex, kInt, kHex),
     MakeEntry(380, "preadv2", kInt, kHex, kInt, kInt, kInt, kHex),
     MakeEntry(381, "pwritev2", kInt, kHex, kInt, kInt, kInt, kHex),
+    MakeEntry(435, "clone3", kHex, kInt),
+    MakeEntry(436, "close_range", kInt, kInt, kHex),
+    MakeEntry(437, "openat2", kInt, kPath, kHex, kInt),
+    MakeEntry(438, "pidfd_getfd", UnknownArguments()),
+    MakeEntry(439, "faccessat2", kInt, kPath, kHex, kHex),
+    MakeEntry(440, "process_madvise", UnknownArguments()),
+    MakeEntry(441, "epoll_pwait2", UnknownArguments()),
+    MakeEntry(442, "mount_setattr", UnknownArguments()),
+    MakeEntry(443, "quotactl_fd", UnknownArguments()),
+    MakeEntry(444, "landlock_create_ruleset", UnknownArguments()),
+    MakeEntry(445, "landlock_add_rule", UnknownArguments()),
+    MakeEntry(446, "landlock_restrict_self", UnknownArguments()),
+    MakeEntry(448, "process_mrelease", UnknownArguments()),
+    MakeEntry(449, "futex_waitv", UnknownArguments()),
+    MakeEntry(450, "set_mempolicy_home_node", UnknownArguments()),
+    MakeEntry(451, "cachestat", UnknownArguments()),
+    MakeEntry(452, "fchmodat2", kInt, kPath, kHex, kHex),
+    MakeEntry(453, "map_shadow_stack", UnknownArguments()),
+    MakeEntry(454, "futex_wake", UnknownArguments()),
+    MakeEntry(455, "futex_wait", UnknownArguments()),
+    MakeEntry(456, "futex_requeue", UnknownArguments()),
+    MakeEntry(457, "statmount", UnknownArguments()),
+    MakeEntry(458, "listmount", UnknownArguments()),
+    MakeEntry(459, "lsm_get_self_attr", UnknownArguments()),
+    MakeEntry(460, "lsm_set_self_attr", UnknownArguments()),
+    MakeEntry(461, "lsm_list_modules", UnknownArguments()),
     // clang-format on
 };
 
@@ -1520,6 +1586,32 @@
     MakeEntry(285, "copy_file_range", kInt, kHex, kInt, kHex, kInt, kHex),
     MakeEntry(286, "preadv2", kInt, kHex, kInt, kInt, kInt, kHex),
     MakeEntry(287, "pwritev2", kInt, kHex, kInt, kInt, kInt, kHex),
+    MakeEntry(435, "clone3", kHex, kInt),
+    MakeEntry(436, "close_range", kInt, kInt, kHex),
+    MakeEntry(437, "openat2", kInt, kPath, kHex, kInt),
+    MakeEntry(438, "pidfd_getfd", UnknownArguments()),
+    MakeEntry(439, "faccessat2", kInt, kPath, kHex, kHex),
+    MakeEntry(440, "process_madvise", UnknownArguments()),
+    MakeEntry(441, "epoll_pwait2", UnknownArguments()),
+    MakeEntry(442, "mount_setattr", UnknownArguments()),
+    MakeEntry(443, "quotactl_fd", UnknownArguments()),
+    MakeEntry(444, "landlock_create_ruleset", UnknownArguments()),
+    MakeEntry(445, "landlock_add_rule", UnknownArguments()),
+    MakeEntry(446, "landlock_restrict_self", UnknownArguments()),
+    MakeEntry(448, "process_mrelease", UnknownArguments()),
+    MakeEntry(449, "futex_waitv", UnknownArguments()),
+    MakeEntry(450, "set_mempolicy_home_node", UnknownArguments()),
+    MakeEntry(451, "cachestat", UnknownArguments()),
+    MakeEntry(452, "fchmodat2", kInt, kPath, kHex, kHex),
+    MakeEntry(453, "map_shadow_stack", UnknownArguments()),
+    MakeEntry(454, "futex_wake", UnknownArguments()),
+    MakeEntry(455, "futex_wait", UnknownArguments()),
+    MakeEntry(456, "futex_requeue", UnknownArguments()),
+    MakeEntry(457, "statmount", UnknownArguments()),
+    MakeEntry(458, "listmount", UnknownArguments()),
+    MakeEntry(459, "lsm_get_self_attr", UnknownArguments()),
+    MakeEntry(460, "lsm_set_self_attr", UnknownArguments()),
+    MakeEntry(461, "lsm_list_modules", UnknownArguments()),
     // clang-format on
 };
 
@@ -1877,6 +1969,32 @@
     MakeEntry(393, "pwritev2", kInt, kHex, kInt, kInt, kInt, kHex),
     MakeEntry(400, "migrate_pages", kGen, kGen, kGen, kGen),
     MakeEntry(401, "kexec_file_load", kInt, kInt, kInt, kString, kHex),
+    MakeEntry(435, "clone3", kHex, kInt),
+    MakeEntry(436, "close_range", kInt, kInt, kHex),
+    MakeEntry(437, "openat2", kInt, kPath, kHex, kInt),
+    MakeEntry(438, "pidfd_getfd", UnknownArguments()),
+    MakeEntry(439, "faccessat2", kInt, kPath, kHex, kHex),
+    MakeEntry(440, "process_madvise", UnknownArguments()),
+    MakeEntry(441, "epoll_pwait2", UnknownArguments()),
+    MakeEntry(442, "mount_setattr", UnknownArguments()),
+    MakeEntry(443, "quotactl_fd", UnknownArguments()),
+    MakeEntry(444, "landlock_create_ruleset", UnknownArguments()),
+    MakeEntry(445, "landlock_add_rule", UnknownArguments()),
+    MakeEntry(446, "landlock_restrict_self", UnknownArguments()),
+    MakeEntry(448, "process_mrelease", UnknownArguments()),
+    MakeEntry(449, "futex_waitv", UnknownArguments()),
+    MakeEntry(450, "set_mempolicy_home_node", UnknownArguments()),
+    MakeEntry(451, "cachestat", UnknownArguments()),
+    MakeEntry(452, "fchmodat2", kInt, kPath, kHex, kHex),
+    MakeEntry(453, "map_shadow_stack", UnknownArguments()),
+    MakeEntry(454, "futex_wake", UnknownArguments()),
+    MakeEntry(455, "futex_wait", UnknownArguments()),
+    MakeEntry(456, "futex_requeue", UnknownArguments()),
+    MakeEntry(457, "statmount", UnknownArguments()),
+    MakeEntry(458, "listmount", UnknownArguments()),
+    MakeEntry(459, "lsm_get_self_attr", UnknownArguments()),
+    MakeEntry(460, "lsm_set_self_attr", UnknownArguments()),
+    MakeEntry(461, "lsm_list_modules", UnknownArguments()),
     MakeEntry(0xf0001, "ARM_breakpoint", kHex, kHex, kHex, kHex),
     MakeEntry(0xf0002, "ARM_cacheflush", kHex, kHex, kHex, kHex),
     MakeEntry(0xf0003, "ARM_usr26", kHex, kHex, kHex, kHex),
@@ -1889,19 +2007,20 @@
               "Syscalls should be sorted");
 
 }  // namespace
+}  // namespace syscalls
 
 SyscallTable SyscallTable::get(sapi::cpu::Architecture arch) {
   switch (arch) {
     case sapi::cpu::kX8664:
-      return SyscallTable(kSyscallDataX8664);
+      return SyscallTable(syscalls::kSyscallDataX8664);
     case sapi::cpu::kX86:
-      return SyscallTable(kSyscallDataX8632);
+      return SyscallTable(syscalls::kSyscallDataX8632);
     case sapi::cpu::kPPC64LE:
-      return SyscallTable(kSyscallDataPPC64LE);
+      return SyscallTable(syscalls::kSyscallDataPPC64LE);
     case sapi::cpu::kArm64:
-      return SyscallTable(kSyscallDataArm64);
+      return SyscallTable(syscalls::kSyscallDataArm64);
     case sapi::cpu::kArm:
-      return SyscallTable(kSyscallDataArm32);
+      return SyscallTable(syscalls::kSyscallDataArm32);
     default:
       return SyscallTable();
   }
diff --git a/sandboxed_api/sandbox2/syscall_defs.h b/sandboxed_api/sandbox2/syscall_defs.h
index a5e7fc3..1fbf3dc 100644
--- a/sandboxed_api/sandbox2/syscall_defs.h
+++ b/sandboxed_api/sandbox2/syscall_defs.h
@@ -3,10 +3,12 @@
 
 #include <sys/types.h>
 
+#include <array>
 #include <cstdint>
 #include <string>
 #include <vector>
 
+#include "absl/status/statusor.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/span.h"
 #include "sandboxed_api/config.h"
@@ -17,11 +19,51 @@
 
 constexpr int kMaxArgs = 6;
 
+// Type of a given syscall argument. Used with argument conversion routines.
+enum ArgType {
+  kGen = 1,
+  kInt,
+  kPath,
+  kHex,
+  kOct,
+  kSocketCall,
+  kSocketCallPtr,
+  kSignal,
+  kString,
+  kAddressFamily,
+  kSockaddr,
+  kSockmsghdr,
+  kCloneFlag,
+};
+
 }  // namespace syscalls
 
 class SyscallTable {
  public:
-  struct Entry;
+  // Single syscall definition
+  struct Entry {
+    // Returns the number of arguments which given syscall takes.
+    int GetNumArgs() const {
+      if (num_args < 0 || num_args > syscalls::kMaxArgs) {
+        return syscalls::kMaxArgs;
+      }
+      return num_args;
+    }
+
+    static std::string GetArgumentDescription(uint64_t value,
+                                              syscalls::ArgType type,
+                                              pid_t pid);
+
+    static constexpr bool BySyscallNr(const SyscallTable::Entry& a,
+                                      const SyscallTable::Entry& b) {
+      return a.nr < b.nr;
+    }
+
+    int nr;
+    absl::string_view name;
+    int num_args;
+    std::array<syscalls::ArgType, syscalls::kMaxArgs> arg_types;
+  };
 
   // Returns the syscall table for the architecture.
   static SyscallTable get(sapi::cpu::Architecture arch);
@@ -34,6 +76,12 @@
                                                    const uint64_t values[],
                                                    pid_t pid) const;
 
+  absl::StatusOr<Entry> GetEntry(int syscall) const;
+  // Returns the first entry matching the provided name.
+  absl::StatusOr<Entry> GetEntry(absl::string_view name) const;
+
+  absl::Span<const Entry> GetEntries() const { return data_; }
+
  private:
   constexpr SyscallTable() = default;
   explicit constexpr SyscallTable(absl::Span<const Entry> data) : data_(data) {}
diff --git a/sandboxed_api/sandbox2/testcases/BUILD.bazel b/sandboxed_api/sandbox2/testcases/BUILD.bazel
index 92cedb8..7ba8141 100644
--- a/sandboxed_api/sandbox2/testcases/BUILD.bazel
+++ b/sandboxed_api/sandbox2/testcases/BUILD.bazel
@@ -261,3 +261,16 @@
         "@com_google_absl//absl/strings:string_view",
     ],
 )
+
+cc_binary(
+    name = "custom_fork",
+    testonly = True,
+    srcs = ["custom_fork.cc"],
+    copts = sapi_platform_copts(),
+    features = ["fully_static_link"],
+    deps = [
+        "//sandboxed_api/sandbox2:comms",
+        "//sandboxed_api/sandbox2:forkingclient",
+        "//sandboxed_api/util:raw_logging",
+    ],
+)
diff --git a/sandboxed_api/sandbox2/testcases/CMakeLists.txt b/sandboxed_api/sandbox2/testcases/CMakeLists.txt
index 3dfd039..43bde70 100644
--- a/sandboxed_api/sandbox2/testcases/CMakeLists.txt
+++ b/sandboxed_api/sandbox2/testcases/CMakeLists.txt
@@ -322,3 +322,19 @@
   sapi::status
 )
 
+# sandboxed_api/sandbox2/testcases:custom_fork
+add_executable(sandbox2_testcase_custom_fork
+  custom_fork.cc
+)
+add_executable(sandbox2::testcase_custom_fork ALIAS sandbox2_testcase_custom_fork)
+set_target_properties(sandbox2_testcase_custom_fork PROPERTIES
+  OUTPUT_NAME custom_fork
+)
+target_link_libraries(sandbox2_testcase_custom_fork PRIVATE
+  sandbox2::comms
+  sandbox2::forkingclient
+  sapi::base
+  sapi::raw_logging
+)
+
+
diff --git a/sandboxed_api/sandbox2/testcases/custom_fork.cc b/sandboxed_api/sandbox2/testcases/custom_fork.cc
new file mode 100644
index 0000000..3625229
--- /dev/null
+++ b/sandboxed_api/sandbox2/testcases/custom_fork.cc
@@ -0,0 +1,34 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "sandboxed_api/sandbox2/comms.h"
+#include "sandboxed_api/sandbox2/forkingclient.h"
+#include "sandboxed_api/util/raw_logging.h"
+
+int main(int argc, char* argv[]) {
+  sandbox2::Comms comms(sandbox2::Comms::kDefaultConnection);
+  sandbox2::ForkingClient s2client(&comms);
+
+  for (;;) {
+    pid_t pid = s2client.WaitAndFork();
+    if (pid == -1) {
+      SAPI_RAW_LOG(FATAL, "Could not spawn a new sandboxee");
+    }
+    if (pid == 0) {
+      // Start sandboxing here
+      s2client.SandboxMeHere();
+      return 0;
+    }
+  }
+}
diff --git a/sandboxed_api/sandbox2/testcases/starve.cc b/sandboxed_api/sandbox2/testcases/starve.cc
index 6903c0a..5295e76 100644
--- a/sandboxed_api/sandbox2/testcases/starve.cc
+++ b/sandboxed_api/sandbox2/testcases/starve.cc
@@ -40,7 +40,7 @@
 
 int main() {
   for (int i = 0; i < kProcesses; ++i) {
-    int p[2];
+    int p[2] = {0, 0};
     char c = ' ';
     pipe(p);
     g_pids[i] = fork();
diff --git a/sandboxed_api/sandbox2/trace_all_syscalls.h b/sandboxed_api/sandbox2/trace_all_syscalls.h
new file mode 100644
index 0000000..d387cc3
--- /dev/null
+++ b/sandboxed_api/sandbox2/trace_all_syscalls.h
@@ -0,0 +1,27 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SANDBOXED_API_SANDBOX2_TRACE_ALL_SYSCALLS_H_
+#define SANDBOXED_API_SANDBOX2_TRACE_ALL_SYSCALLS_H_
+
+namespace sandbox2 {
+
+class TraceAllSyscalls {
+ public:
+  explicit TraceAllSyscalls() = default;
+};
+
+}  // namespace sandbox2
+
+#endif  // SANDBOXED_API_SANDBOX2_ALLOW_ALL_SYSCALLS_H_
diff --git a/sandboxed_api/sandbox2/util/BUILD.bazel b/sandboxed_api/sandbox2/util/BUILD.bazel
index 86cc785..89bc522 100644
--- a/sandboxed_api/sandbox2/util/BUILD.bazel
+++ b/sandboxed_api/sandbox2/util/BUILD.bazel
@@ -32,8 +32,6 @@
     name = "bpf_helper",
     srcs = ["bpf_helper.c"],
     hdrs = ["bpf_helper.h"],
-    copts = sapi_platform_copts([
-    ]),
     visibility = ["//visibility:public"],
 )
 
diff --git a/sandboxed_api/sapi_test.cc b/sandboxed_api/sapi_test.cc
index 5f66f2c..6aa25ab 100644
--- a/sandboxed_api/sapi_test.cc
+++ b/sandboxed_api/sapi_test.cc
@@ -284,5 +284,24 @@
   EXPECT_THAT(result.final_status(), Eq(sandbox2::Result::EXTERNAL_KILL));
 }
 
+TEST(SandboxTest, UseUnotifyMonitor) {
+  SumSandbox sandbox;
+  ASSERT_THAT(sandbox.Init(/*use_unotify_monitor=*/true), IsOk());
+  SumApi api(&sandbox);
+
+  // Violate the sandbox policy.
+  EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
+  EXPECT_THAT(api.sum(1, 2).status(), StatusIs(absl::StatusCode::kUnavailable));
+  EXPECT_THAT(sandbox.AwaitResult().final_status(),
+              Eq(sandbox2::Result::VIOLATION));
+
+  // Restart the sandbox.
+  ASSERT_THAT(sandbox.Restart(false), IsOk());
+
+  // The sandbox should now be responsive again.
+  SAPI_ASSERT_OK_AND_ASSIGN(int result, api.sum(1, 2));
+  EXPECT_THAT(result, Eq(3));
+}
+
 }  // namespace
 }  // namespace sapi
diff --git a/sandboxed_api/tools/generator2/code_test.py b/sandboxed_api/tools/generator2/code_test.py
index de74485..8ef6057 100644
--- a/sandboxed_api/tools/generator2/code_test.py
+++ b/sandboxed_api/tools/generator2/code_test.py
@@ -89,7 +89,7 @@
         if x.kind != cindex.CursorKind.MACRO_DEFINITION
     ]
     self.assertListEqual(cursor_kinds, [
-        cindex.CursorKind.TRANSLATION_UNIT, cindex.CursorKind.UNEXPOSED_DECL,
+        cindex.CursorKind.TRANSLATION_UNIT, cindex.CursorKind.LINKAGE_SPEC,
         cindex.CursorKind.FUNCTION_DECL, cindex.CursorKind.PARM_DECL
     ])
 
diff --git a/sandboxed_api/var_ptr.h b/sandboxed_api/var_ptr.h
index ad2568c..2c9894f 100644
--- a/sandboxed_api/var_ptr.h
+++ b/sandboxed_api/var_ptr.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <string>
 
+#include "absl/base/attributes.h"
 #include "absl/base/macros.h"
 #include "absl/strings/str_format.h"
 #include "sandboxed_api/var_abstract.h"
@@ -79,7 +80,9 @@
 };
 
 // Good, old nullptr
-class NullPtr : public Ptr {
+class ABSL_DEPRECATED(
+    "Use regular `nullptr` or `NULL` instead. This class will eventually get "
+    "removed") NullPtr : public Ptr {
  public:
   NullPtr() : Ptr(&void_obj_, SyncType::kSyncNone) {}