Thread (29 messages) 29 messages, 14 authors, 2020-07-24

Re: strace of io_uring events?

From: Andy Lutomirski <luto@kernel.org>
Date: 2020-07-16 16:25:08
Also in: io-uring, linux-fsdevel, lkml

On Jul 15, 2020, at 4:07 PM, Kees Cook [off-list ref] wrote:

Earlier Andy Lutomirski wrote:
quoted
Let’s add some seccomp folks. We probably also want to be able to run
seccomp-like filters on io_uring requests. So maybe io_uring should call into
seccomp-and-tracing code for each action.
Okay, I'm finally able to spend time looking at this. And thank you to
the many people that CCed me into this and earlier discussions (at least
Jann, Christian, and Andy).

It *seems* like there is a really clean mapping of SQE OPs to syscalls.
To that end, yes, it should be trivial to add ptrace and seccomp support
(sort of). The trouble comes for doing _interception_, which is how both
ptrace and seccomp are designed.

In the basic case of seccomp, various syscalls are just being checked
for accept/reject. It seems like that would be easy to wire up. For the
more ptrace-y things (SECCOMP_RET_TRAP, SECCOMP_RET_USER_NOTIF, etc),
I think any such results would need to be "upgraded" to "reject". Things
are a bit complex in that seccomp's form of "reject" can be "return
errno" (easy) or it can be "kill thread (or thread_group)" which ...
becomes less clear. (More on this later.)
My intuition is not to do this kind of creative reinterpretation of
return values. Instead let’s have a new type of seccomp filter
specifically for io_uring. So we can have SECCOMP_IO_URING_ACCEPT,
ERRNO, and eventually other things. We probably will want a user
notifier feature for io_uring, but I'd be a bit surprised if it ends
up ABI-compatible with current users of user notifiers.
quoted hunk ↗ jump to hunk
- There appear to be three classes of desired restrictions:
 - opcodes for io_uring_register() (which can be enforced entirely with
   seccomp right now).
Agreed.
 - opcodes from SQEs (this _could_ be intercepted by seccomp, but is
   not currently written)
As above, I think this should be intercepted by seccomp, but in a new
mode.  I think that existing seccomp filters should not intercept it.
 - opcodes of the types of restrictions to restrict... for making sure
   things can't be changed after being set? seccomp already enforces
   that kind of "can only be made stricter"
Agreed.
quoted hunk ↗ jump to hunk

- How does no_new_privs play a role in the existing io_uring credential
 management? Using _any_ kind of syscall-effective filtering, whether
 it's seccomp or Stefano's existing proposal, needs to address the
 potential inheritable restrictions across privilege boundaries (which is
 what no_new_privs tries to eliminate). In regular syscall land, this is
 an issue when a filter follows a process through setuid via execve()
 and it gains privileges that now the filter-creator can trick into
 doing weird stuff -- io_uring has a concept of alternative credentials
 so I have to ask about it. (I don't *think* there would be a path to
 install a filter before gaining privilege, but I likely just
 need to do my homework on the io_uring internals. Regardless,
 use of seccomp by io_uring would need to have this issue "solved"
 in the sense that it must be "safe" to filter io_uring OPs, from a
 privilege-boundary-crossing perspective.

- From which task perspective should filters be applied? It seems like it
 needs to follow the io_uring personalities, as that contains the
 credentials. (This email is a brain-dump so far -- I haven't gone to
 look to see if that means io_uring is literally getting a reference to
 struct cred; I assume so.) Seccomp filters are attached to task_struct.
 However, for v5.9, seccomp will gain a more generalized get/put system
 for having filters attached to the SECCOMP_RET_USER_NOTIF fd. Adding
 more get/put-ers for some part of the io_uring context shouldn't
 be hard.
Let's ignore personalities for a moment (and see below).  Thinking
through the possibilities:

A: io_uring seccomp filters are attached to tasks.  When an io_uring
is created, it inherits an immutable copy of its creating task's
filter, and that's the filter set that applies to that io_uring
instance.  This could have somewhat bizarre consequences if the fd
gets passed around, but io_uring already has odd security effects if
fds are passed around.  It has the annoying property that, if a
library creates an io_uring and then a seccomp filter is loaded, the
io_uring bypasses the library.

B: The same, but the io_uring references the creating task so new
filters on the task apply to the io_uring, too.  This allows loading
and then sandboxing.  Is this too bizarre overall?

C: io_uring filters are attached directly to io_urings.  This has the
problem where an io_uring created before a task sandboxes itself isn't
sandboxed.  It also would require that a filter be able to hook
io_uring creation to sandbox it.

Does anyone actually pass io_urings around with SCM_RIGHTS?  It would
be really nice if we could make the default be that io_urings are
bound to their creating mm and can't be used outside it.  Then
creating an mm-crossing io_uring could, itself, be restricted.

In any case, my inclination is to go for choice B.  Choice C could
also be supported if there's a use case.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help