Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83a50840 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull seccomp fixes from Kees Cook:
 "Syzbot found a use-after-free bug in seccomp due to flags that should
  not be allowed to be used together.

  Tycho fixed this, I updated the self-tests, and the syzkaller PoC has
  been running for several days without triggering KASan (before this
  fix, it would reproduce). These patches have also been in -next for
  almost a week, just to be sure.

   - Add logic for making some seccomp flags exclusive (Tycho)

   - Update selftests for exclusivity testing (Kees)"

* tag 'seccomp-v5.1-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  seccomp: Make NEW_LISTENER and TSYNC flags exclusive
  selftests/seccomp: Prepare for exclusive seccomp flags
parents 80871482 7a0df7fb
Loading
Loading
Loading
Loading
+15 −2
Original line number Original line Diff line number Diff line
@@ -502,7 +502,10 @@ seccomp_prepare_user_filter(const char __user *user_filter)
 *
 *
 * Caller must be holding current->sighand->siglock lock.
 * Caller must be holding current->sighand->siglock lock.
 *
 *
 * Returns 0 on success, -ve on error.
 * Returns 0 on success, -ve on error, or
 *   - in TSYNC mode: the pid of a thread which was either not in the correct
 *     seccomp mode or did not have an ancestral seccomp filter
 *   - in NEW_LISTENER mode: the fd of the new listener
 */
 */
static long seccomp_attach_filter(unsigned int flags,
static long seccomp_attach_filter(unsigned int flags,
				  struct seccomp_filter *filter)
				  struct seccomp_filter *filter)
@@ -1258,6 +1261,16 @@ static long seccomp_set_mode_filter(unsigned int flags,
	if (flags & ~SECCOMP_FILTER_FLAG_MASK)
	if (flags & ~SECCOMP_FILTER_FLAG_MASK)
		return -EINVAL;
		return -EINVAL;


	/*
	 * In the successful case, NEW_LISTENER returns the new listener fd.
	 * But in the failure case, TSYNC returns the thread that died. If you
	 * combine these two flags, there's no way to tell whether something
	 * succeeded or failed. So, let's disallow this combination.
	 */
	if ((flags & SECCOMP_FILTER_FLAG_TSYNC) &&
	    (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER))
		return -EINVAL;

	/* Prepare the new filter before holding any locks. */
	/* Prepare the new filter before holding any locks. */
	prepared = seccomp_prepare_user_filter(filter);
	prepared = seccomp_prepare_user_filter(filter);
	if (IS_ERR(prepared))
	if (IS_ERR(prepared))
@@ -1304,7 +1317,7 @@ static long seccomp_set_mode_filter(unsigned int flags,
		mutex_unlock(&current->signal->cred_guard_mutex);
		mutex_unlock(&current->signal->cred_guard_mutex);
out_put_fd:
out_put_fd:
	if (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) {
	if (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) {
		if (ret < 0) {
		if (ret) {
			listener_f->private_data = NULL;
			listener_f->private_data = NULL;
			fput(listener_f);
			fput(listener_f);
			put_unused_fd(listener);
			put_unused_fd(listener);
+25 −9
Original line number Original line Diff line number Diff line
@@ -2166,11 +2166,14 @@ TEST(detect_seccomp_filter_flags)
				 SECCOMP_FILTER_FLAG_LOG,
				 SECCOMP_FILTER_FLAG_LOG,
				 SECCOMP_FILTER_FLAG_SPEC_ALLOW,
				 SECCOMP_FILTER_FLAG_SPEC_ALLOW,
				 SECCOMP_FILTER_FLAG_NEW_LISTENER };
				 SECCOMP_FILTER_FLAG_NEW_LISTENER };
	unsigned int flag, all_flags;
	unsigned int exclusive[] = {
				SECCOMP_FILTER_FLAG_TSYNC,
				SECCOMP_FILTER_FLAG_NEW_LISTENER };
	unsigned int flag, all_flags, exclusive_mask;
	int i;
	int i;
	long ret;
	long ret;


	/* Test detection of known-good filter flags */
	/* Test detection of individual known-good filter flags */
	for (i = 0, all_flags = 0; i < ARRAY_SIZE(flags); i++) {
	for (i = 0, all_flags = 0; i < ARRAY_SIZE(flags); i++) {
		int bits = 0;
		int bits = 0;


@@ -2197,16 +2200,29 @@ TEST(detect_seccomp_filter_flags)
		all_flags |= flag;
		all_flags |= flag;
	}
	}


	/* Test detection of all known-good filter flags */
	/*
	ret = seccomp(SECCOMP_SET_MODE_FILTER, all_flags, NULL);
	 * Test detection of all known-good filter flags combined. But
	 * for the exclusive flags we need to mask them out and try them
	 * individually for the "all flags" testing.
	 */
	exclusive_mask = 0;
	for (i = 0; i < ARRAY_SIZE(exclusive); i++)
		exclusive_mask |= exclusive[i];
	for (i = 0; i < ARRAY_SIZE(exclusive); i++) {
		flag = all_flags & ~exclusive_mask;
		flag |= exclusive[i];

		ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
		EXPECT_EQ(-1, ret);
		EXPECT_EQ(-1, ret);
		EXPECT_EQ(EFAULT, errno) {
		EXPECT_EQ(EFAULT, errno) {
			TH_LOG("Failed to detect that all known-good filter flags (0x%X) are supported!",
			TH_LOG("Failed to detect that all known-good filter flags (0x%X) are supported!",
		       all_flags);
			       flag);
		}
	}
	}


	/* Test detection of an unknown filter flag */
	/* Test detection of an unknown filter flags, without exclusives. */
	flag = -1;
	flag = -1;
	flag &= ~exclusive_mask;
	ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
	ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
	EXPECT_EQ(-1, ret);
	EXPECT_EQ(-1, ret);
	EXPECT_EQ(EINVAL, errno) {
	EXPECT_EQ(EINVAL, errno) {