diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c index 0d2b9c646030..4a6b9d99e594 100644 --- a/security/landlock/tsync.c +++ b/security/landlock/tsync.c @@ -447,6 +447,19 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred, shared_ctx.new_cred = new_cred; shared_ctx.set_no_new_privs = task_no_new_privs(current); + /* + * We must serialize concurrent TSYNC requests within the same thread group. + * However, we cannot use a blocking mutex_lock() here. If Thread A holds + * the lock and queues task_work on Thread B, and Thread B calls TSYNC and + * blocks on the lock, Thread B will never return to user-mode to execute + * Thread A's task_work, resulting in a deadlock. + * + * Using mutex_trylock() allows us to fail gracefully, returning + * -ERESTARTNOINTR to process pending task_work and retry the syscall. + */ + if (!mutex_trylock(¤t->signal->cred_guard_mutex)) + return -ERESTARTNOINTR; + /* * We schedule a pseudo-signal task_work for each of the calling task's * sibling threads. In the task work, each thread: @@ -557,5 +570,7 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred, tsync_works_release(&works); + mutex_unlock(¤t->signal->cred_guard_mutex); + return atomic_read(&shared_ctx.preparation_error); }