Varad Gautam a8331c
From a11ddb37bf367e6b5239b95ca759e5389bb46048 Mon Sep 17 00:00:00 2001
Varad Gautam 5e2321
From: Varad Gautam <varad.gautam@suse.com>
Varad Gautam 5e2321
Date: Fri, 14 May 2021 10:39:22 +1000
Varad Gautam 5e2321
Subject: ipc/mqueue, msg, sem: Avoid relying on a stack reference past its
Varad Gautam 5e2321
 expiry
Varad Gautam 5e2321
References: bsc#1185988
Varad Gautam a8331c
Git-commit: a11ddb37bf367e6b5239b95ca759e5389bb46048
Varad Gautam a8331c
Patch-mainline: v5.13-rc3
Varad Gautam a8331c
Varad Gautam 5e2321
Varad Gautam 5e2321
do_mq_timedreceive calls wq_sleep with a stack local address.  The sender
Varad Gautam 5e2321
(do_mq_timedsend) uses this address to later call pipelined_send.
Varad Gautam 5e2321
Varad Gautam 5e2321
This leads to a very hard to trigger race where a do_mq_timedreceive call
Varad Gautam 5e2321
might return and leave do_mq_timedsend to rely on an invalid address,
Varad Gautam 5e2321
causing the following crash:
Varad Gautam 5e2321
Varad Gautam 5e2321
[  240.739977] RIP: 0010:wake_q_add_safe+0x13/0x60
Varad Gautam 5e2321
[  240.739991] Call Trace:
Varad Gautam 5e2321
[  240.739999]  __x64_sys_mq_timedsend+0x2a9/0x490
Varad Gautam 5e2321
[  240.740003]  ? auditd_test_task+0x38/0x40
Varad Gautam 5e2321
[  240.740007]  ? auditd_test_task+0x38/0x40
Varad Gautam 5e2321
[  240.740011]  do_syscall_64+0x80/0x680
Varad Gautam 5e2321
[  240.740017]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
Varad Gautam 5e2321
[  240.740019] RIP: 0033:0x7f5928e40343
Varad Gautam 5e2321
Varad Gautam 5e2321
The race occurs as:
Varad Gautam 5e2321
Varad Gautam 5e2321
1. do_mq_timedreceive calls wq_sleep with the address of `struct
Varad Gautam 5e2321
   ext_wait_queue` on function stack (aliased as `ewq_addr` here) - it
Varad Gautam 5e2321
   holds a valid `struct ext_wait_queue *` as long as the stack has not
Varad Gautam 5e2321
   been overwritten.
Varad Gautam 5e2321
Varad Gautam 5e2321
2. `ewq_addr` gets added to info->e_wait_q[RECV].list in wq_add, and
Varad Gautam 5e2321
   do_mq_timedsend receives it via wq_get_first_waiter(info, RECV) to call
Varad Gautam 5e2321
   __pipelined_op.
Varad Gautam 5e2321
Varad Gautam 5e2321
3. Sender calls __pipelined_op::smp_store_release(&this->state,
Varad Gautam 5e2321
   STATE_READY).  Here is where the race window begins.  (`this` is
Varad Gautam 5e2321
   `ewq_addr`.)
Varad Gautam 5e2321
Varad Gautam 5e2321
4. If the receiver wakes up now in do_mq_timedreceive::wq_sleep, it
Varad Gautam 5e2321
   will see `state == STATE_READY` and break.
Varad Gautam 5e2321
Varad Gautam 5e2321
5. do_mq_timedreceive returns, and `ewq_addr` is no longer guaranteed
Varad Gautam 5e2321
   to be a `struct ext_wait_queue *` since it was on do_mq_timedreceive's
Varad Gautam 5e2321
   stack.  (Although the address may not get overwritten until another
Varad Gautam 5e2321
   function happens to touch it, which means it can persist around for an
Varad Gautam 5e2321
   indefinite time.)
Varad Gautam 5e2321
Varad Gautam 5e2321
6. do_mq_timedsend::__pipelined_op() still believes `ewq_addr` is a
Varad Gautam 5e2321
   `struct ext_wait_queue *`, and uses it to find a task_struct to pass to
Varad Gautam 5e2321
   the wake_q_add_safe call.  In the lucky case where nothing has
Varad Gautam 5e2321
   overwritten `ewq_addr` yet, `ewq_addr->task` is the right task_struct.
Varad Gautam 5e2321
   In the unlucky case, __pipelined_op::wake_q_add_safe gets handed a
Varad Gautam 5e2321
   bogus address as the receiver's task_struct causing the crash.
Varad Gautam 5e2321
Varad Gautam 5e2321
do_mq_timedsend::__pipelined_op() should not dereference `this` after
Varad Gautam 5e2321
setting STATE_READY, as the receiver counterpart is now free to return.
Varad Gautam 5e2321
Change __pipelined_op to call wake_q_add_safe on the receiver's
Varad Gautam 5e2321
task_struct returned by get_task_struct, instead of dereferencing `this`
Varad Gautam 5e2321
which sits on the receiver's stack.
Varad Gautam 5e2321
Varad Gautam 5e2321
As Manfred pointed out, the race potentially also exists in
Varad Gautam 5e2321
ipc/msg.c::expunge_all and ipc/sem.c::wake_up_sem_queue_prepare.  Fix
Varad Gautam 5e2321
those in the same way.
Varad Gautam 5e2321
Varad Gautam 5e2321
Link: https://lkml.kernel.org/r/20210510102950.12551-1-varad.gautam@suse.com
Varad Gautam 5e2321
Fixes: c5b2cbdbdac563 ("ipc/mqueue.c: update/document memory barriers")
Varad Gautam 5e2321
Fixes: 8116b54e7e23ef ("ipc/sem.c: document and update memory barriers")
Varad Gautam 5e2321
Fixes: 0d97a82ba830d8 ("ipc/msg.c: update and document memory barriers")
Varad Gautam 5e2321
Signed-off-by: Varad Gautam <varad.gautam@suse.com>
Varad Gautam 5e2321
Reported-by: Matthias von Faber <matthias.vonfaber@aox-tech.de>
Varad Gautam 5e2321
Acked-by: Davidlohr Bueso <dbueso@suse.de>
Varad Gautam 5e2321
Acked-by: Manfred Spraul <manfred@colorfullife.com>
Varad Gautam 5e2321
Cc: Christian Brauner <christian.brauner@ubuntu.com>
Varad Gautam 5e2321
Cc: Oleg Nesterov <oleg@redhat.com>
Varad Gautam 5e2321
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Varad Gautam 5e2321
Cc: <stable@vger.kernel.org>
Varad Gautam 5e2321
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Varad Gautam 5e2321
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Varad Gautam 5e2321
Varad Gautam 5e2321
---
Varad Gautam 5e2321
 ipc/mqueue.c | 6 ++++--
Varad Gautam 5e2321
 ipc/msg.c    | 6 ++++--
Varad Gautam 5e2321
 ipc/sem.c    | 6 ++++--
Varad Gautam 5e2321
 3 files changed, 12 insertions(+), 6 deletions(-)
Varad Gautam 5e2321
Varad Gautam 5e2321
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
Varad Gautam 5e2321
index 8031464ed4ae2..4e4e61111500c 100644
Varad Gautam 5e2321
--- a/ipc/mqueue.c
Varad Gautam 5e2321
+++ b/ipc/mqueue.c
Varad Gautam 5e2321
@@ -1004,12 +1004,14 @@ static inline void __pipelined_op(struct wake_q_head *wake_q,
Varad Gautam 5e2321
 				  struct mqueue_inode_info *info,
Varad Gautam 5e2321
 				  struct ext_wait_queue *this)
Varad Gautam 5e2321
 {
Varad Gautam 5e2321
+	struct task_struct *task;
Varad Gautam 5e2321
+
Varad Gautam 5e2321
 	list_del(&this->list);
Varad Gautam 5e2321
-	get_task_struct(this->task);
Varad Gautam 5e2321
+	task = get_task_struct(this->task);
Varad Gautam 5e2321
 
Varad Gautam 5e2321
 	/* see MQ_BARRIER for purpose/pairing */
Varad Gautam 5e2321
 	smp_store_release(&this->state, STATE_READY);
Varad Gautam 5e2321
-	wake_q_add_safe(wake_q, this->task);
Varad Gautam 5e2321
+	wake_q_add_safe(wake_q, task);
Varad Gautam 5e2321
 }
Varad Gautam 5e2321
 
Varad Gautam 5e2321
 /* pipelined_send() - send a message directly to the task waiting in
Varad Gautam 5e2321
diff --git a/ipc/msg.c b/ipc/msg.c
Varad Gautam 5e2321
index acd1bc7af55a2..6e6c8e0c9380e 100644
Varad Gautam 5e2321
--- a/ipc/msg.c
Varad Gautam 5e2321
+++ b/ipc/msg.c
Varad Gautam 5e2321
@@ -251,11 +251,13 @@ static void expunge_all(struct msg_queue *msq, int res,
Varad Gautam 5e2321
 	struct msg_receiver *msr, *t;
Varad Gautam 5e2321
 
Varad Gautam 5e2321
 	list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
Varad Gautam 5e2321
-		get_task_struct(msr->r_tsk);
Varad Gautam 5e2321
+		struct task_struct *r_tsk;
Varad Gautam 5e2321
+
Varad Gautam 5e2321
+		r_tsk = get_task_struct(msr->r_tsk);
Varad Gautam 5e2321
 
Varad Gautam 5e2321
 		/* see MSG_BARRIER for purpose/pairing */
Varad Gautam 5e2321
 		smp_store_release(&msr->r_msg, ERR_PTR(res));
Varad Gautam 5e2321
-		wake_q_add_safe(wake_q, msr->r_tsk);
Varad Gautam 5e2321
+		wake_q_add_safe(wake_q, r_tsk);
Varad Gautam 5e2321
 	}
Varad Gautam 5e2321
 }
Varad Gautam 5e2321
 
Varad Gautam 5e2321
diff --git a/ipc/sem.c b/ipc/sem.c
Varad Gautam 5e2321
index e0ec239680cbd..bf534c74293e1 100644
Varad Gautam 5e2321
--- a/ipc/sem.c
Varad Gautam 5e2321
+++ b/ipc/sem.c
Varad Gautam 5e2321
@@ -784,12 +784,14 @@ would_block:
Varad Gautam 5e2321
 static inline void wake_up_sem_queue_prepare(struct sem_queue *q, int error,
Varad Gautam 5e2321
 					     struct wake_q_head *wake_q)
Varad Gautam 5e2321
 {
Varad Gautam 5e2321
-	get_task_struct(q->sleeper);
Varad Gautam 5e2321
+	struct task_struct *sleeper;
Varad Gautam 5e2321
+
Varad Gautam 5e2321
+	sleeper = get_task_struct(q->sleeper);
Varad Gautam 5e2321
 
Varad Gautam 5e2321
 	/* see SEM_BARRIER_2 for purpuse/pairing */
Varad Gautam 5e2321
 	smp_store_release(&q->status, error);
Varad Gautam 5e2321
 
Varad Gautam 5e2321
-	wake_q_add_safe(wake_q, q->sleeper);
Varad Gautam 5e2321
+	wake_q_add_safe(wake_q, sleeper);
Varad Gautam 5e2321
 }
Varad Gautam 5e2321
 
Varad Gautam 5e2321
 static void unlink_queue(struct sem_array *sma, struct sem_queue *q)
Varad Gautam 5e2321
-- 
Varad Gautam 5e2321
cgit 1.2.3-1.el7
Varad Gautam 5e2321