Skip to content

Commit e2790fe

Browse files
committed
landlock: Always allow signals between threads of the same process
JIRA: https://issues.redhat.com/browse/RHEL-125143 Upstream Status: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Conflicts: Replaced file_f_owner due to missing 1934b21 ("file: reclaim 24 bytes from f_owner"). Minor context difference. commit 18eb75f Author: Mickaël Salaün <mic@digikod.net> Date: Tue Mar 18 17:14:40 2025 +0100 landlock: Always allow signals between threads of the same process Because Linux credentials are managed per thread, user space relies on some hack to synchronize credential update across threads from the same process. This is required by the Native POSIX Threads Library and implemented by set*id(2) wrappers and libcap(3) to use tgkill(2) to synchronize threads. See nptl(7) and libpsx(3). Furthermore, some runtimes like Go do not enable developers to have control over threads [1]. To avoid potential issues, and because threads are not security boundaries, let's relax the Landlock (optional) signal scoping to always allow signals sent between threads of the same process. This exception is similar to the __ptrace_may_access() one. hook_file_set_fowner() now checks if the target task is part of the same process as the caller. If this is the case, then the related signal triggered by the socket will always be allowed. Scoping of abstract UNIX sockets is not changed because kernel objects (e.g. sockets) should be tied to their creator's domain at creation time. Note that creating one Landlock domain per thread puts each of these threads (and their future children) in their own scope, which is probably not what users expect, especially in Go where we do not control threads. However, being able to drop permissions on all threads should not be restricted by signal scoping. We are working on a way to make it possible to atomically restrict all threads of a process with the same domain [2]. Add erratum for signal scoping. Closes: landlock-lsm/go-landlock#36 Fixes: 54a6e6b ("landlock: Add signal scoping") Fixes: c899496 ("selftests/landlock: Test signal scoping for threads") Depends-on: 26f2043 ("fs: Fix file_set_fowner LSM hook inconsistencies") Link: https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/psx [1] Link: landlock-lsm/linux#2 [2] Cc: Günther Noack <gnoack@google.com> Cc: Paul Moore <paul@paul-moore.com> Cc: Serge Hallyn <serge@hallyn.com> Cc: Tahera Fahimi <fahimitahera@gmail.com> Cc: stable@vger.kernel.org Acked-by: Christian Brauner <brauner@kernel.org> Link: https://lore.kernel.org/r/20250318161443.279194-6-mic@digikod.net [mic: Add extra pointer check and RCU guard, and ease backport] Signed-off-by: Mickaël Salaün <mic@digikod.net> Signed-off-by: Štěpán Horáček <shoracek@redhat.com>
1 parent 2eb6cec commit e2790fe

File tree

4 files changed

+65
-7
lines changed

4 files changed

+65
-7
lines changed

security/landlock/errata/abi-6.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
/**
4+
* DOC: erratum_2
5+
*
6+
* Erratum 2: Scoped signal handling
7+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8+
*
9+
* This fix addresses an issue where signal scoping was overly restrictive,
10+
* preventing sandboxed threads from signaling other threads within the same
11+
* process if they belonged to different domains. Because threads are not
12+
* security boundaries, user space might assume that any thread within the same
13+
* process can send signals between themselves (see :manpage:`nptl(7)` and
14+
* :manpage:`libpsx(3)`). Consistent with :manpage:`ptrace(2)` behavior, direct
15+
* interaction between threads of the same process should always be allowed.
16+
* This change ensures that any thread is allowed to send signals to any other
17+
* thread within the same process, regardless of their domain.
18+
*/
19+
LANDLOCK_ERRATUM(2)

security/landlock/fs.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
#include <linux/mount.h>
2828
#include <linux/namei.h>
2929
#include <linux/path.h>
30+
#include <linux/pid.h>
3031
#include <linux/rcupdate.h>
32+
#include <linux/sched/signal.h>
3133
#include <linux/spinlock.h>
3234
#include <linux/stat.h>
3335
#include <linux/types.h>
@@ -1636,21 +1638,46 @@ static int hook_file_ioctl_compat(struct file *file, unsigned int cmd,
16361638
return -EACCES;
16371639
}
16381640

1639-
static void hook_file_set_fowner(struct file *file)
1641+
/*
1642+
* Always allow sending signals between threads of the same process. This
1643+
* ensures consistency with hook_task_kill().
1644+
*/
1645+
static bool control_current_fowner(struct fown_struct *const fown)
16401646
{
1641-
struct landlock_ruleset *new_dom, *prev_dom;
1647+
struct task_struct *p;
16421648

16431649
/*
16441650
* Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix
16451651
* file_set_fowner LSM hook inconsistencies").
16461652
*/
1647-
lockdep_assert_held(&file->f_owner.lock);
1648-
new_dom = landlock_get_current_domain();
1649-
landlock_get_ruleset(new_dom);
1653+
lockdep_assert_held(&fown->lock);
1654+
1655+
/*
1656+
* Some callers (e.g. fcntl_dirnotify) may not be in an RCU read-side
1657+
* critical section.
1658+
*/
1659+
guard(rcu)();
1660+
p = pid_task(fown->pid, fown->pid_type);
1661+
if (!p)
1662+
return true;
1663+
1664+
return !same_thread_group(p, current);
1665+
}
1666+
1667+
static void hook_file_set_fowner(struct file *file)
1668+
{
1669+
struct landlock_ruleset *prev_dom;
1670+
struct landlock_ruleset *new_dom = NULL;
1671+
1672+
if (control_current_fowner(&file->f_owner)) {
1673+
new_dom = landlock_get_current_domain();
1674+
landlock_get_ruleset(new_dom);
1675+
}
1676+
16501677
prev_dom = landlock_file(file)->fown_domain;
16511678
landlock_file(file)->fown_domain = new_dom;
16521679

1653-
/* Called in an RCU read-side critical section. */
1680+
/* May be called in an RCU read-side critical section. */
16541681
landlock_put_ruleset_deferred(prev_dom);
16551682
}
16561683

security/landlock/task.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/lsm_hooks.h>
1414
#include <linux/rcupdate.h>
1515
#include <linux/sched.h>
16+
#include <linux/sched/signal.h>
1617
#include <net/af_unix.h>
1718
#include <net/sock.h>
1819

@@ -254,6 +255,17 @@ static int hook_task_kill(struct task_struct *const p,
254255
/* Dealing with USB IO. */
255256
dom = landlock_cred(cred)->domain;
256257
} else {
258+
/*
259+
* Always allow sending signals between threads of the same process.
260+
* This is required for process credential changes by the Native POSIX
261+
* Threads Library and implemented by the set*id(2) wrappers and
262+
* libcap(3) with tgkill(2). See nptl(7) and libpsx(3).
263+
*
264+
* This exception is similar to the __ptrace_may_access() one.
265+
*/
266+
if (same_thread_group(p, current))
267+
return 0;
268+
257269
dom = landlock_get_current_domain();
258270
}
259271

tools/testing/selftests/landlock/scoped_signal_test.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ TEST(signal_scoping_threads)
281281
/* Restricts the domain after creating the first thread. */
282282
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
283283

284-
ASSERT_EQ(EPERM, pthread_kill(no_sandbox_thread, 0));
284+
ASSERT_EQ(0, pthread_kill(no_sandbox_thread, 0));
285285
ASSERT_EQ(1, write(thread_pipe[1], ".", 1));
286286

287287
ASSERT_EQ(0, pthread_create(&scoped_thread, NULL, thread_func, NULL));

0 commit comments

Comments
 (0)