seccomp fixes for v5.13-rc4

- Fix addfd notification race condition (Sargun Dhillon)
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmCyhNIACgkQiXL039xt
 wCbExBAAoniF2+pW8sN32KK6a4uLGJCPCcbwZqWGw2zINqn6+I6KGAld37lGPu3E
 ASuu28O45NXcP9SpHLxNT1jRhAet57G6OjSV78jEzVII2EogUIBOyRji7yTk8xCt
 kCp21/9RaQ3DitYe2vh9R2neNIZh/PodmY8V5tkP2HacgaEuf5+yRhB/1QbTm7HG
 +mMZsejw1eEryJ49cw7XkYpWNjyz5vxwvXWJt6nfgm7wTnNopUQUKJGwnp2bX9cZ
 LUgstLq0SpHW7uxwEq4NYux3qsD9kaj5SgZxb/6KkHNmg5q6WUXxm0FljipEIhq1
 RBTLdH+6Ct+DcDryno2VDoRNP/Q3pim9jxTpfQQ5V6f4dVqNv6pVuR2uNfK/iEX2
 mk7Rc99IifaXeOLITKGusZrm16msVg+o7wAu0B1iT0vyacPcwRXJtIWy829Z+gCP
 r5OsBguxPPTkxfoRWYX4WDNcZmuBC5hkyqzN8toiQjOGghdm9nXdH4jFl8kcqZps
 I7i0Me3JBWVskx1d8AKlkJv3ctbdUX7QV/HaPdsMLlXTLyqBR76D/uqeUFgmWpUq
 2ib3bkJzRNYgm2nron1fmDOLTiJGVfEha5hmbThPrVziYv7+jwamHzPf8jPvB+tg
 nOpw/HEfoVQtuq/e+Ocdv6TLnZAZWnvxYC/RB3aTBq5xz+74nYA=
 =c9Hd
 -----END PGP SIGNATURE-----

Merge tag 'seccomp-fixes-v5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull seccomp fixes from Kees Cook:
 "This fixes a hard-to-hit race condition in the addfd user_notif
  feature of seccomp, visible since v5.9.

  And a small documentation fix"

* tag 'seccomp-fixes-v5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  seccomp: Refactor notification handler to prepare for new semantics
  Documentation: seccomp: Fix user notification documentation
This commit is contained in:
Linus Torvalds 2021-05-29 18:16:09 -10:00
commit 9a76c0ee3a
2 changed files with 24 additions and 22 deletions

View File

@ -250,14 +250,14 @@ Users can read via ``ioctl(SECCOMP_IOCTL_NOTIF_RECV)`` (or ``poll()``) on a
seccomp notification fd to receive a ``struct seccomp_notif``, which contains seccomp notification fd to receive a ``struct seccomp_notif``, which contains
five members: the input length of the structure, a unique-per-filter ``id``, five members: the input length of the structure, a unique-per-filter ``id``,
the ``pid`` of the task which triggered this request (which may be 0 if the the ``pid`` of the task which triggered this request (which may be 0 if the
task is in a pid ns not visible from the listener's pid namespace), a ``flags`` task is in a pid ns not visible from the listener's pid namespace). The
member which for now only has ``SECCOMP_NOTIF_FLAG_SIGNALED``, representing notification also contains the ``data`` passed to seccomp, and a filters flag.
whether or not the notification is a result of a non-fatal signal, and the The structure should be zeroed out prior to calling the ioctl.
``data`` passed to seccomp. Userspace can then make a decision based on this
information about what to do, and ``ioctl(SECCOMP_IOCTL_NOTIF_SEND)`` a Userspace can then make a decision based on this information about what to do,
response, indicating what should be returned to userspace. The ``id`` member of and ``ioctl(SECCOMP_IOCTL_NOTIF_SEND)`` a response, indicating what should be
``struct seccomp_notif_resp`` should be the same ``id`` as in ``struct returned to userspace. The ``id`` member of ``struct seccomp_notif_resp`` should
seccomp_notif``. be the same ``id`` as in ``struct seccomp_notif``.
It is worth noting that ``struct seccomp_data`` contains the values of register It is worth noting that ``struct seccomp_data`` contains the values of register
arguments to the syscall, but does not contain pointers to memory. The task's arguments to the syscall, but does not contain pointers to memory. The task's

View File

@ -1105,28 +1105,30 @@ static int seccomp_do_user_notification(int this_syscall,
up(&match->notif->request); up(&match->notif->request);
wake_up_poll(&match->wqh, EPOLLIN | EPOLLRDNORM); wake_up_poll(&match->wqh, EPOLLIN | EPOLLRDNORM);
mutex_unlock(&match->notify_lock);
/* /*
* This is where we wait for a reply from userspace. * This is where we wait for a reply from userspace.
*/ */
wait: do {
err = wait_for_completion_interruptible(&n.ready); mutex_unlock(&match->notify_lock);
mutex_lock(&match->notify_lock); err = wait_for_completion_interruptible(&n.ready);
if (err == 0) { mutex_lock(&match->notify_lock);
/* Check if we were woken up by a addfd message */ if (err != 0)
goto interrupted;
addfd = list_first_entry_or_null(&n.addfd, addfd = list_first_entry_or_null(&n.addfd,
struct seccomp_kaddfd, list); struct seccomp_kaddfd, list);
if (addfd && n.state != SECCOMP_NOTIFY_REPLIED) { /* Check if we were woken up by a addfd message */
if (addfd)
seccomp_handle_addfd(addfd); seccomp_handle_addfd(addfd);
mutex_unlock(&match->notify_lock);
goto wait;
}
ret = n.val;
err = n.error;
flags = n.flags;
}
} while (n.state != SECCOMP_NOTIFY_REPLIED);
ret = n.val;
err = n.error;
flags = n.flags;
interrupted:
/* If there were any pending addfd calls, clear them out */ /* If there were any pending addfd calls, clear them out */
list_for_each_entry_safe(addfd, tmp, &n.addfd, list) { list_for_each_entry_safe(addfd, tmp, &n.addfd, list) {
/* The process went away before we got a chance to handle it */ /* The process went away before we got a chance to handle it */