[RFC] landlock: clarify signal scoping for pre-existing f_owner registrations

From
Shi Lecun <hidden>
Date
2026-11-07 04:24:16
Hi Mickaël and Günther,

While investigating LANDLOCK_SCOPE_SIGNAL, I noticed that the current
documentation does not describe how signal scoping interacts with
asynchronous notifications registered before the scope is enforced.

A minimal reproducer performs the following steps as an unprivileged user:

1. Create a pipe and use fcntl(F_SETOWN) to select a same-UID process
   outside the future Landlock domain as the notification recipient.
2. Configure F_SETSIG and O_ASYNC.
3. Add a Landlock layer handling LANDLOCK_SCOPE_SIGNAL.
4. Write to the pipe.

The configured signal is still delivered to the out-of-domain process.
Using F_SETSIG(SIGKILL) makes this especially visible by terminating the
recipient.  In contrast:

- kill(2) after enforcing LANDLOCK_SCOPE_SIGNAL fails with EPERM;
- registering f_owner after enforcing the scope does not result in
  out-of-domain signal delivery.

I reproduced this matrix on 7.2.14-arch1-1 with Landlock ABI 10 as UID
1000:

  direct kill after scope      victim survives
  post-scope F_SETOWN          victim survives
  pre-scope F_SETOWN           victim receives SIGKILL
  FS layer, F_SETOWN, scope    victim receives SIGKILL

I also inspected a local kernel tree at commit d68ce4e71248.
From security/landlock/fs.c and security/landlock/task.c, my
understanding is that hook_file_set_fowner() saves the applicable
Landlock subject when F_SETOWN is called, and
hook_file_send_sigiotask() later checks delivery using that saved
subject.  If no signal-scoped domain applied at registration time, the
saved domain is NULL and later delivery is not checked against the
triggering task's newly added domain.  The usual UID/EUID checks in
sigio_perm() still apply.

This also appears to be intentional and covered by the existing
fown/sandbox_after_setown variant in
tools/testing/selftests/landlock/scoped_signal_test.c, which expects a
signal to be received when F_SETOWN happened before sandboxing.  I
therefore do not consider this behavior itself to be a signal-scope
bypass.

However, Documentation/userspace-api/landlock.rst currently says:

  "This limits the sending of signals to target processes which run
  within the same or a nested Landlock domain."

Without a lifecycle qualification, this can be read as covering every
signal delivered after the scope is enforced, including notifications
whose f_owner was registered earlier.  The same section explicitly
documents the lifetime exception for previously connected UNIX sockets,
but there is no corresponding explanation for asynchronous signals.

Would it make sense to add a paragraph along these lines?

  Asynchronous signal notifications associated with a file owner, such
  as SIGIO and SIGURG, are checked using the Landlock context saved
  when fcntl(F_SETOWN) sets the notification recipient. Adding a
  LANDLOCK_SCOPE_SIGNAL restriction later does not apply that new
  restriction to notifications using the previously saved context.
  Sandboxes that require signal isolation should therefore enforce
  the scope before untrusted code can register asynchronous
  notification recipients, and account for any pre-existing
  registrations on retained file descriptors.

Does this accurately describe the intended userspace semantics?
If so, I would be happy to send a documentation patch for the IPC
scoping section.

Thanks,
Shi Lecun
Thread (4 messages) 4 messages, 2 authors, last reply 13h ago
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help