| From 4271b05a227dc6175b66c3d9941aeab09048aeb2 Mon Sep 17 00:00:00 2001 |
| From: Davidlohr Bueso <davidlohr@hp.com> |
| Date: Mon, 30 Sep 2013 13:45:26 -0700 |
| Subject: ipc,msg: prevent race with rmid in msgsnd,msgrcv |
| |
| From: Davidlohr Bueso <davidlohr@hp.com> |
| |
| commit 4271b05a227dc6175b66c3d9941aeab09048aeb2 upstream. |
| |
| This fixes a race in both msgrcv() and msgsnd() between finding the msg |
| and actually dealing with the queue, as another thread can delete shmid |
| underneath us if we are preempted before acquiring the |
| kern_ipc_perm.lock. |
| |
| Manfred illustrates this nicely: |
| |
| Assume a preemptible kernel that is preempted just after |
| |
| msq = msq_obtain_object_check(ns, msqid) |
| |
| in do_msgrcv(). The only lock that is held is rcu_read_lock(). |
| |
| Now the other thread processes IPC_RMID. When the first task is |
| resumed, then it will happily wait for messages on a deleted queue. |
| |
| Fix this by checking for if the queue has been deleted after taking the |
| lock. |
| |
| Signed-off-by: Davidlohr Bueso <davidlohr@hp.com> |
| Reported-by: Manfred Spraul <manfred@colorfullife.com> |
| Cc: Rik van Riel <riel@redhat.com> |
| Cc: Mike Galbraith <efault@gmx.de> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| ipc/msg.c | 13 +++++++++++++ |
| 1 file changed, 13 insertions(+) |
| |
| --- a/ipc/msg.c |
| +++ b/ipc/msg.c |
| @@ -689,6 +689,12 @@ long do_msgsnd(int msqid, long mtype, vo |
| if (ipcperms(ns, &msq->q_perm, S_IWUGO)) |
| goto out_unlock0; |
| |
| + /* raced with RMID? */ |
| + if (msq->q_perm.deleted) { |
| + err = -EIDRM; |
| + goto out_unlock0; |
| + } |
| + |
| err = security_msg_queue_msgsnd(msq, msg, msgflg); |
| if (err) |
| goto out_unlock0; |
| @@ -895,6 +901,13 @@ long do_msgrcv(int msqid, void __user *b |
| goto out_unlock1; |
| |
| ipc_lock_object(&msq->q_perm); |
| + |
| + /* raced with RMID? */ |
| + if (msq->q_perm.deleted) { |
| + msg = ERR_PTR(-EIDRM); |
| + goto out_unlock0; |
| + } |
| + |
| msg = find_msg(msq, &msgtyp, mode); |
| if (!IS_ERR(msg)) { |
| /* |