about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-07-24 19:45:52 +0200
committerbptato <nincsnevem662@gmail.com>2024-07-24 19:46:49 +0200
commitde2a70dc814658a8c72e7da6180ea5e16a8b985b (patch)
treec089a85e65d0069e2d51b12d1553ada8c2a63ea6
parent40122721d3b25d3b7e75f93e64d88853d1c9c0f2 (diff)
downloadchawan-de2a70dc814658a8c72e7da6180ea5e16a8b985b.tar.gz
client, sandbox: fix termux build
Still not perfect, because it crashes on missing /tmp dir so you have to
manually set it...
-rw-r--r--src/local/client.nim13
-rw-r--r--src/utils/sandbox.nim54
-rw-r--r--todo1
3 files changed, 60 insertions, 8 deletions
diff --git a/src/local/client.nim b/src/local/client.nim
index 97390528..1fc5a7f1 100644
--- a/src/local/client.nim
+++ b/src/local/client.nim
@@ -359,7 +359,8 @@ proc input(client: Client): EmptyPromise =
     p.resolve()
   return p
 
-let SIGWINCH {.importc, header: "<signal.h>", nodecl.}: cint
+when not defined(android):
+  let SIGWINCH {.importc, header: "<signal.h>", nodecl.}: cint
 
 proc showConsole(client: Client) {.jsfunc.} =
   let container = client.consoleWrapper.container
@@ -546,7 +547,8 @@ proc handleError(client: Client; fd: int) =
 proc inputLoop(client: Client) =
   let selector = client.selector
   selector.registerHandle(int(client.pager.term.istream.fd), {Read}, 0)
-  let sigwinch = selector.registerSignal(int(SIGWINCH), 0)
+  when not defined(android):
+    let sigwinch = selector.registerSignal(int(SIGWINCH), 0)
   while true:
     let events = client.selector.select(-1)
     for event in events:
@@ -556,9 +558,10 @@ proc inputLoop(client: Client) =
         client.handleWrite(event.fd)
       if Error in event.events:
         client.handleError(event.fd)
-      if Signal in event.events:
-        assert event.fd == sigwinch
-        client.pager.windowChange()
+      when not defined(android):
+        if Signal in event.events:
+          assert event.fd == sigwinch
+          client.pager.windowChange()
       if selectors.Event.Timer in event.events:
         let r = client.timeouts.runTimeoutFd(event.fd)
         assert r
diff --git a/src/utils/sandbox.nim b/src/utils/sandbox.nim
index efc03e49..19e6fd0a 100644
--- a/src/utils/sandbox.nim
+++ b/src/utils/sandbox.nim
@@ -81,6 +81,53 @@ elif SandboxMode == stLibSeccomp:
   import std/posix
   import bindings/libseccomp
 
+  when defined(android):
+    let PR_SET_VMA {.importc, header: "<sys/prctl.h>", nodecl.}: cint
+    let PR_SET_VMA_ANON_NAME {.importc, header: "<sys/prctl.h>", nodecl.}: cint
+
+    proc allowBionic(ctx: scmp_filter_ctx) =
+      # Things needed for bionic libc. Tested with Termux.
+      const androidAllowList = [
+        cstring"rt_sigprocmask",
+        "epoll_pwait",
+        "futex",
+        "madvise"
+      ]
+      for it in androidAllowList:
+        let syscall = seccomp_syscall_resolve_name(it)
+        doAssert seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0) == 0
+      # bionic likes to set this very much. In fact, it was added to
+      # the kernel by Android devs.
+      block allowAnonVMAName:
+        let syscall = seccomp_syscall_resolve_name("prctl")
+        let arg0 = scmp_arg_cmp(
+          arg: 0, # op
+          op: SCMP_CMP_EQ, # equals
+          datum_a: uint64(PR_SET_VMA)
+        )
+        let arg1 = scmp_arg_cmp(
+          arg: 1, # attr
+          op: SCMP_CMP_EQ, # equals
+          datum_a: uint64(PR_SET_VMA_ANON_NAME)
+        )
+        doAssert seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 2, arg0,
+          arg1) == 0
+      # We have to be careful with this one; PROT_EXEC will happily set
+      # memory as executable, which is certainly not what we want.
+      # Now, bionic seems to be calling this from mutate(), ergo we
+      # should be fine just allowing PROT_READ and PROT_READ | PROT_WRITE.
+      block allowMprotect:
+        let syscall = seccomp_syscall_resolve_name("mprotect")
+        let arg2 = scmp_arg_cmp(
+          arg: 2, # attr
+          op: SCMP_CMP_LE, # less or equals
+          datum_a: 3 # PROT_READ | PROT_WRITE
+        )
+        # Note that libseccomp can't really express multiple comparisons.
+        # However, we are lucky, and we only have to "excessively" allow
+        # PROT_WRITE (w/o PROT_READ) and PROT_NONE, which does no harm.
+        doAssert seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 1, arg2) == 0
+
   proc enterBufferSandbox*(sockPath: string) =
     onSignal SIGSYS:
       discard sig
@@ -137,6 +184,8 @@ elif SandboxMode == stLibSeccomp:
         datum_a: 1 # PF_LOCAL == PF_UNIX == AF_UNIX
       )
       doAssert seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 1, arg0) == 0
+    when defined(android):
+      ctx.allowBionic()
     doAssert seccomp_load(ctx) == 0
     seccomp_release(ctx)
 
@@ -155,9 +204,6 @@ elif SandboxMode == stLibSeccomp:
       "poll", # curl needs poll
       "getpid", # used indirectly by OpenSSL EVP_RAND_CTX_new (through drbg)
       "fstat", # glibc fread seems to call it
-      # maybe it will need epoll too in the future
-      "epoll_create", "epoll_create1", "epoll_ctl", "epoll_wait",
-      "ppoll", # or ppoll
       # we either have to use CURLOPT_NOSIGNAL or allow signals.
       # do the latter, otherwise the default name resolver will never time out.
       "signal", "sigaction", "rt_sigaction",
@@ -165,6 +211,8 @@ elif SandboxMode == stLibSeccomp:
     for it in allowList:
       doAssert seccomp_rule_add(ctx, SCMP_ACT_ALLOW,
         seccomp_syscall_resolve_name(it), 0) == 0
+    when defined(android):
+      ctx.allowBionic()
     doAssert seccomp_load(ctx) == 0
     seccomp_release(ctx)
 else:
diff --git a/todo b/todo
index 1c924ee4..0f7ee84f 100644
--- a/todo
+++ b/todo
@@ -13,6 +13,7 @@ display:
 - dark mode (basically max Y)
 - override bgcolor ourselves when terminal fails to report it
 config:
+- important: fix crash on missing /tmp dir with default config
 - important: config editor
 - completely replace siteconf; the new solution should:
 	* not be based on table arrays