summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--security/landlock/fs.c14
-rw-r--r--security/landlock/fs.h10
-rw-r--r--security/landlock/task.c11
3 files changed, 35 insertions, 0 deletions
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index c1ecfe239032..664962a416d7 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1901,6 +1901,14 @@ static bool control_current_fowner(struct fown_struct *const fown)
lockdep_assert_held(&fown->lock);
/*
+ * A process-group or session owner (PIDTYPE_PGID/PIDTYPE_SID) fans the
+ * signal out to every member at delivery time, so record the domain and
+ * let hook_file_send_sigiotask() check the live scope per recipient.
+ */
+ if (fown->pid_type != PIDTYPE_PID && fown->pid_type != PIDTYPE_TGID)
+ return true;
+
+ /*
* Some callers (e.g. fcntl_dirnotify) may not be in an RCU read-side
* critical section.
*/
@@ -1916,6 +1924,7 @@ static void hook_file_set_fowner(struct file *file)
{
struct landlock_ruleset *prev_dom;
struct landlock_cred_security fown_subject = {};
+ struct pid *prev_tg, *fown_tg = NULL;
size_t fown_layer = 0;
if (control_current_fowner(file_f_owner(file))) {
@@ -1928,21 +1937,26 @@ static void hook_file_set_fowner(struct file *file)
if (new_subject) {
landlock_get_ruleset(new_subject->domain);
fown_subject = *new_subject;
+ fown_tg = get_pid(task_tgid(current));
}
}
prev_dom = landlock_file(file)->fown_subject.domain;
+ prev_tg = landlock_file(file)->fown_tg;
landlock_file(file)->fown_subject = fown_subject;
+ landlock_file(file)->fown_tg = fown_tg;
#ifdef CONFIG_AUDIT
landlock_file(file)->fown_layer = fown_layer;
#endif /* CONFIG_AUDIT*/
/* May be called in an RCU read-side critical section. */
landlock_put_ruleset_deferred(prev_dom);
+ put_pid(prev_tg);
}
static void hook_file_free_security(struct file *file)
{
+ put_pid(landlock_file(file)->fown_tg);
landlock_put_ruleset_deferred(landlock_file(file)->fown_subject.domain);
}
diff --git a/security/landlock/fs.h b/security/landlock/fs.h
index bf9948941f2f..911b83669e20 100644
--- a/security/landlock/fs.h
+++ b/security/landlock/fs.h
@@ -78,6 +78,16 @@ struct landlock_file_security {
* euid.
*/
struct landlock_cred_security fown_subject;
+ /**
+ * @fown_tg: Thread group of the task that set the file owner, pinned
+ * while @fown_subject holds a domain. It lets
+ * hook_file_send_sigiotask() always allow a SIGIO delivered to the
+ * owner's own process -- e.g. the thread-group leader reached through a
+ * process-group owner -- matching the same-process exemption of
+ * hook_task_kill(). NULL when no domain is recorded. Protected by
+ * file->f_owner->lock, like @fown_subject.
+ */
+ struct pid *fown_tg;
};
#ifdef CONFIG_AUDIT
diff --git a/security/landlock/task.c b/security/landlock/task.c
index 6d46042132ce..7ddf211f75c3 100644
--- a/security/landlock/task.c
+++ b/security/landlock/task.c
@@ -411,6 +411,17 @@ static int hook_file_send_sigiotask(struct task_struct *tsk,
if (!subject->domain)
return 0;
+ /*
+ * Always allow delivery to the file owner's own process, including a
+ * thread-group leader reached through a process-group owner. This
+ * mirrors hook_task_kill()'s same-process exemption and preserves the
+ * guarantee of commit 18eb75f3af40 ("landlock: Always allow signals
+ * between threads of the same process"), which the registration-time
+ * check cannot honor for a process-group target.
+ */
+ if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg)
+ return 0;
+
scoped_guard(rcu)
{
is_scoped = domain_is_scoped(subject->domain,