jart / pledge

OpenBSD APIs ported to Linux userspace using SECCOMP BPF and Landlock LSM

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Confusing output (no error message) when tty is not pledged but program queries ioctl TIOCGWINSZ

rsms opened this issue · comments

Busybox programs queries TIOCGWINSZ on startup, meaning benign commands like seq needs to pledge "tty". Failing to do so causes pledge to "silently" fail, printing "Bad system call" (no error messages reported.)

$ pledge -p "stdio" /bin/seq 1
Bad system call
$ pledge -p "stdio tty" /bin/seq 1
1

Original issue:


On a barebones musl (Linux 6.5.8)

$ pledge -p "stdio" /bin/date
Bad system call
$ pledge -p "stdio exec" /bin/date
Mon Apr 29 22:44:12 UTC 2024

The culprit seems to be __pledge_mode & PLEDGE_STDERR_LOGGING

__pledge_mode |= PLEDGE_STDERR_LOGGING;

since if I pass -q to pledge, exec works as expected:

$ pledge -q -p "stdio" /bin/date
Mon Apr 29 22:45:30 UTC 2024

Additionally, passing -k to pledge (sets __pledge_mode = PLEDGE_PENALTY_KILL_PROCESS) also results in "Bad system call", even with -q:

$ pledge -k -p "stdio" /bin/date
Bad system call
$ pledge -k -q -p "stdio" /bin/date
Bad system call

If I disable "implicit exec" by commenting out these lines...

pledge/cmd/pledge.c

Lines 838 to 843 in 8693ebe

if (!(~ipromises & (1ul << PROMISE_EXEC))) {
g_promises = xstrcat(g_promises, ' ', "exec");
if (!g_qflag) {
__pledge_mode |= PLEDGE_STDERR_LOGGING;
}
}

... execv fails with the expected "Operation not permitted" error message:

$ pledge -p "stdio" /bin/date
/bin/date: execve failed: Operation not permitted

The least messy fix I've come up with is to always include a SECCOMP_RET_ERRNO filter:
Edit: I realize now that this patch breaks stuff (as it returns early)

--- a/libc/calls/pledge-linux.c	2023-11-08 18:18:17.000000000 +0000
+++ b/libc/calls/pledge-linux.c	2024-04-29 23:10:44.057225745 +0000
@@ -2129,6 +2129,9 @@
     }
   }

+  sf[0].k = SECCOMP_RET_ERRNO;
+  AppendFilter(&f, PLEDGE(sf));
+
   // now determine what we'll do on sandbox violations
   if (mode & PLEDGE_STDERR_LOGGING) {
     // trapping mode

(Note: /bin/date in this example is a static executable, so sandbox.so is not involved)

Linux 6.5.8 is built with:

CONFIG_HAVE_ARCH_SECCOMP=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_SECCOMP=y
CONFIG_SECCOMP_FILTER=y

Pledging tty and rpath makes it work as well:

$ pledge -p "stdio" /bin/date
Bad system call
$ pledge -p "stdio rpath" /bin/date
Bad system call
$ pledge -p "stdio tty" /bin/date
Bad system call
$ pledge -p "stdio rpath tty" /bin/date
Mon Apr 29 23:36:15 UTC 2024

/bin/date is busybox — perhaps busybox reads the file system and calls e.g. SYS_ioctl TIOCGWINSZ on every invocation..?

Edit: Ah, yes indeed it reads /etc/localtime and queries TIOCGWINSZ:

$ strace /bin/date
execve("/bin/date", ["/bin/date"], 0xffffe81ab850 /* 18 vars */) = 0
set_tid_address(0x3b4a50)               = 29898
getuid()                                = 0
openat(AT_FDCWD, "/etc/localtime", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=114, ...}) = 0
mmap(NULL, 114, PROT_READ, MAP_SHARED, 3, 0) = 0xffff89e4b000
close(3)                                = 0
ioctl(1, TIOCGWINSZ, {ws_row=55, ws_col=100, ws_xpixel=0, ws_ypixel=0}) = 0
writev(1, [{iov_base="Mon Apr 29 23:37:55 UTC 2024", iov_len=28}, {iov_base="\n", iov_len=1}], 2Mon Apr 29 23:37:55 UTC 2024
) = 29
exit_group(0)                           = ?
+++ exited with 0 +++