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 @@ -1900,6 +1900,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,