diff options
| author | Eric Paris <eparis@redhat.com> | 2013-11-22 18:57:08 -0500 |
|---|---|---|
| committer | Eric Paris <eparis@redhat.com> | 2013-11-22 18:57:54 -0500 |
| commit | fc582aef7dcc27a7120cf232c1e76c569c7b6eab (patch) | |
| tree | 7d275dd4ceab6067b91e9a25a5f6338b425fbccd /ipc/msg.c | |
| parent | 9175c9d2aed528800175ef81c90569d00d23f9be (diff) | |
| parent | 5e01dc7b26d9f24f39abace5da98ccbd6a5ceb52 (diff) | |
Merge tag 'v3.12'
Linux 3.12
Conflicts:
fs/exec.c
Diffstat (limited to 'ipc/msg.c')
| -rw-r--r-- | ipc/msg.c | 69 |
1 files changed, 43 insertions, 26 deletions
| @@ -70,8 +70,6 @@ struct msg_sender { | |||
| 70 | 70 | ||
| 71 | #define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) | 71 | #define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) |
| 72 | 72 | ||
| 73 | #define msg_unlock(msq) ipc_unlock(&(msq)->q_perm) | ||
| 74 | |||
| 75 | static void freeque(struct ipc_namespace *, struct kern_ipc_perm *); | 73 | static void freeque(struct ipc_namespace *, struct kern_ipc_perm *); |
| 76 | static int newque(struct ipc_namespace *, struct ipc_params *); | 74 | static int newque(struct ipc_namespace *, struct ipc_params *); |
| 77 | #ifdef CONFIG_PROC_FS | 75 | #ifdef CONFIG_PROC_FS |
| @@ -167,12 +165,21 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) | |||
| 167 | ipc_rmid(&msg_ids(ns), &s->q_perm); | 165 | ipc_rmid(&msg_ids(ns), &s->q_perm); |
| 168 | } | 166 | } |
| 169 | 167 | ||
| 168 | static void msg_rcu_free(struct rcu_head *head) | ||
| 169 | { | ||
| 170 | struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); | ||
| 171 | struct msg_queue *msq = ipc_rcu_to_struct(p); | ||
| 172 | |||
| 173 | security_msg_queue_free(msq); | ||
| 174 | ipc_rcu_free(head); | ||
| 175 | } | ||
| 176 | |||
| 170 | /** | 177 | /** |
| 171 | * newque - Create a new msg queue | 178 | * newque - Create a new msg queue |
| 172 | * @ns: namespace | 179 | * @ns: namespace |
| 173 | * @params: ptr to the structure that contains the key and msgflg | 180 | * @params: ptr to the structure that contains the key and msgflg |
| 174 | * | 181 | * |
| 175 | * Called with msg_ids.rw_mutex held (writer) | 182 | * Called with msg_ids.rwsem held (writer) |
| 176 | */ | 183 | */ |
| 177 | static int newque(struct ipc_namespace *ns, struct ipc_params *params) | 184 | static int newque(struct ipc_namespace *ns, struct ipc_params *params) |
| 178 | { | 185 | { |
| @@ -191,15 +198,14 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) | |||
| 191 | msq->q_perm.security = NULL; | 198 | msq->q_perm.security = NULL; |
| 192 | retval = security_msg_queue_alloc(msq); | 199 | retval = security_msg_queue_alloc(msq); |
| 193 | if (retval) { | 200 | if (retval) { |
| 194 | ipc_rcu_putref(msq); | 201 | ipc_rcu_putref(msq, ipc_rcu_free); |
| 195 | return retval; | 202 | return retval; |
| 196 | } | 203 | } |
| 197 | 204 | ||
| 198 | /* ipc_addid() locks msq upon success. */ | 205 | /* ipc_addid() locks msq upon success. */ |
| 199 | id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); | 206 | id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); |
| 200 | if (id < 0) { | 207 | if (id < 0) { |
| 201 | security_msg_queue_free(msq); | 208 | ipc_rcu_putref(msq, msg_rcu_free); |
| 202 | ipc_rcu_putref(msq); | ||
| 203 | return id; | 209 | return id; |
| 204 | } | 210 | } |
| 205 | 211 | ||
| @@ -259,8 +265,8 @@ static void expunge_all(struct msg_queue *msq, int res) | |||
| 259 | * removes the message queue from message queue ID IDR, and cleans up all the | 265 | * removes the message queue from message queue ID IDR, and cleans up all the |
| 260 | * messages associated with this queue. | 266 | * messages associated with this queue. |
| 261 | * | 267 | * |
| 262 | * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held | 268 | * msg_ids.rwsem (writer) and the spinlock for this message queue are held |
| 263 | * before freeque() is called. msg_ids.rw_mutex remains locked on exit. | 269 | * before freeque() is called. msg_ids.rwsem remains locked on exit. |
| 264 | */ | 270 | */ |
| 265 | static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | 271 | static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) |
| 266 | { | 272 | { |
| @@ -270,19 +276,19 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | |||
| 270 | expunge_all(msq, -EIDRM); | 276 | expunge_all(msq, -EIDRM); |
| 271 | ss_wakeup(&msq->q_senders, 1); | 277 | ss_wakeup(&msq->q_senders, 1); |
| 272 | msg_rmid(ns, msq); | 278 | msg_rmid(ns, msq); |
| 273 | msg_unlock(msq); | 279 | ipc_unlock_object(&msq->q_perm); |
| 280 | rcu_read_unlock(); | ||
| 274 | 281 | ||
| 275 | list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) { | 282 | list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) { |
| 276 | atomic_dec(&ns->msg_hdrs); | 283 | atomic_dec(&ns->msg_hdrs); |
| 277 | free_msg(msg); | 284 | free_msg(msg); |
| 278 | } | 285 | } |
| 279 | atomic_sub(msq->q_cbytes, &ns->msg_bytes); | 286 | atomic_sub(msq->q_cbytes, &ns->msg_bytes); |
| 280 | security_msg_queue_free(msq); | 287 | ipc_rcu_putref(msq, msg_rcu_free); |
| 281 | ipc_rcu_putref(msq); | ||
| 282 | } | 288 | } |
| 283 | 289 | ||
| 284 | /* | 290 | /* |
| 285 | * Called with msg_ids.rw_mutex and ipcp locked. | 291 | * Called with msg_ids.rwsem and ipcp locked. |
| 286 | */ | 292 | */ |
| 287 | static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) | 293 | static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) |
| 288 | { | 294 | { |
| @@ -386,9 +392,9 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) | |||
| 386 | } | 392 | } |
| 387 | 393 | ||
| 388 | /* | 394 | /* |
| 389 | * This function handles some msgctl commands which require the rw_mutex | 395 | * This function handles some msgctl commands which require the rwsem |
| 390 | * to be held in write mode. | 396 | * to be held in write mode. |
| 391 | * NOTE: no locks must be held, the rw_mutex is taken inside this function. | 397 | * NOTE: no locks must be held, the rwsem is taken inside this function. |
| 392 | */ | 398 | */ |
| 393 | static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | 399 | static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, |
| 394 | struct msqid_ds __user *buf, int version) | 400 | struct msqid_ds __user *buf, int version) |
| @@ -403,7 +409,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | |||
| 403 | return -EFAULT; | 409 | return -EFAULT; |
| 404 | } | 410 | } |
| 405 | 411 | ||
| 406 | down_write(&msg_ids(ns).rw_mutex); | 412 | down_write(&msg_ids(ns).rwsem); |
| 407 | rcu_read_lock(); | 413 | rcu_read_lock(); |
| 408 | 414 | ||
| 409 | ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd, | 415 | ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd, |
| @@ -459,7 +465,7 @@ out_unlock0: | |||
| 459 | out_unlock1: | 465 | out_unlock1: |
| 460 | rcu_read_unlock(); | 466 | rcu_read_unlock(); |
| 461 | out_up: | 467 | out_up: |
| 462 | up_write(&msg_ids(ns).rw_mutex); | 468 | up_write(&msg_ids(ns).rwsem); |
| 463 | return err; | 469 | return err; |
| 464 | } | 470 | } |
| 465 | 471 | ||
| @@ -494,7 +500,7 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, | |||
| 494 | msginfo.msgmnb = ns->msg_ctlmnb; | 500 | msginfo.msgmnb = ns->msg_ctlmnb; |
| 495 | msginfo.msgssz = MSGSSZ; | 501 | msginfo.msgssz = MSGSSZ; |
| 496 | msginfo.msgseg = MSGSEG; | 502 | msginfo.msgseg = MSGSEG; |
| 497 | down_read(&msg_ids(ns).rw_mutex); | 503 | down_read(&msg_ids(ns).rwsem); |
| 498 | if (cmd == MSG_INFO) { | 504 | if (cmd == MSG_INFO) { |
| 499 | msginfo.msgpool = msg_ids(ns).in_use; | 505 | msginfo.msgpool = msg_ids(ns).in_use; |
| 500 | msginfo.msgmap = atomic_read(&ns->msg_hdrs); | 506 | msginfo.msgmap = atomic_read(&ns->msg_hdrs); |
| @@ -505,7 +511,7 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, | |||
| 505 | msginfo.msgtql = MSGTQL; | 511 | msginfo.msgtql = MSGTQL; |
| 506 | } | 512 | } |
| 507 | max_id = ipc_get_maxid(&msg_ids(ns)); | 513 | max_id = ipc_get_maxid(&msg_ids(ns)); |
| 508 | up_read(&msg_ids(ns).rw_mutex); | 514 | up_read(&msg_ids(ns).rwsem); |
| 509 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) | 515 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) |
| 510 | return -EFAULT; | 516 | return -EFAULT; |
| 511 | return (max_id < 0) ? 0 : max_id; | 517 | return (max_id < 0) ? 0 : max_id; |
| @@ -680,16 +686,24 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
| 680 | goto out_unlock1; | 686 | goto out_unlock1; |
| 681 | } | 687 | } |
| 682 | 688 | ||
| 689 | ipc_lock_object(&msq->q_perm); | ||
| 690 | |||
| 683 | for (;;) { | 691 | for (;;) { |
| 684 | struct msg_sender s; | 692 | struct msg_sender s; |
| 685 | 693 | ||
| 686 | err = -EACCES; | 694 | err = -EACCES; |
| 687 | if (ipcperms(ns, &msq->q_perm, S_IWUGO)) | 695 | if (ipcperms(ns, &msq->q_perm, S_IWUGO)) |
| 688 | goto out_unlock1; | 696 | goto out_unlock0; |
| 697 | |||
| 698 | /* raced with RMID? */ | ||
| 699 | if (msq->q_perm.deleted) { | ||
| 700 | err = -EIDRM; | ||
| 701 | goto out_unlock0; | ||
| 702 | } | ||
| 689 | 703 | ||
| 690 | err = security_msg_queue_msgsnd(msq, msg, msgflg); | 704 | err = security_msg_queue_msgsnd(msq, msg, msgflg); |
| 691 | if (err) | 705 | if (err) |
| 692 | goto out_unlock1; | 706 | goto out_unlock0; |
| 693 | 707 | ||
| 694 | if (msgsz + msq->q_cbytes <= msq->q_qbytes && | 708 | if (msgsz + msq->q_cbytes <= msq->q_qbytes && |
| 695 | 1 + msq->q_qnum <= msq->q_qbytes) { | 709 | 1 + msq->q_qnum <= msq->q_qbytes) { |
| @@ -699,10 +713,9 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
| 699 | /* queue full, wait: */ | 713 | /* queue full, wait: */ |
| 700 | if (msgflg & IPC_NOWAIT) { | 714 | if (msgflg & IPC_NOWAIT) { |
| 701 | err = -EAGAIN; | 715 | err = -EAGAIN; |
| 702 | goto out_unlock1; | 716 | goto out_unlock0; |
| 703 | } | 717 | } |
| 704 | 718 | ||
| 705 | ipc_lock_object(&msq->q_perm); | ||
| 706 | ss_add(msq, &s); | 719 | ss_add(msq, &s); |
| 707 | 720 | ||
| 708 | if (!ipc_rcu_getref(msq)) { | 721 | if (!ipc_rcu_getref(msq)) { |
| @@ -717,7 +730,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
| 717 | rcu_read_lock(); | 730 | rcu_read_lock(); |
| 718 | ipc_lock_object(&msq->q_perm); | 731 | ipc_lock_object(&msq->q_perm); |
| 719 | 732 | ||
| 720 | ipc_rcu_putref(msq); | 733 | ipc_rcu_putref(msq, ipc_rcu_free); |
| 721 | if (msq->q_perm.deleted) { | 734 | if (msq->q_perm.deleted) { |
| 722 | err = -EIDRM; | 735 | err = -EIDRM; |
| 723 | goto out_unlock0; | 736 | goto out_unlock0; |
| @@ -730,10 +743,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
| 730 | goto out_unlock0; | 743 | goto out_unlock0; |
| 731 | } | 744 | } |
| 732 | 745 | ||
| 733 | ipc_unlock_object(&msq->q_perm); | ||
| 734 | } | 746 | } |
| 735 | |||
| 736 | ipc_lock_object(&msq->q_perm); | ||
| 737 | msq->q_lspid = task_tgid_vnr(current); | 747 | msq->q_lspid = task_tgid_vnr(current); |
| 738 | msq->q_stime = get_seconds(); | 748 | msq->q_stime = get_seconds(); |
| 739 | 749 | ||
| @@ -897,6 +907,13 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl | |||
| 897 | goto out_unlock1; | 907 | goto out_unlock1; |
| 898 | 908 | ||
| 899 | ipc_lock_object(&msq->q_perm); | 909 | ipc_lock_object(&msq->q_perm); |
| 910 | |||
| 911 | /* raced with RMID? */ | ||
| 912 | if (msq->q_perm.deleted) { | ||
| 913 | msg = ERR_PTR(-EIDRM); | ||
| 914 | goto out_unlock0; | ||
| 915 | } | ||
| 916 | |||
| 900 | msg = find_msg(msq, &msgtyp, mode); | 917 | msg = find_msg(msq, &msgtyp, mode); |
| 901 | if (!IS_ERR(msg)) { | 918 | if (!IS_ERR(msg)) { |
| 902 | /* | 919 | /* |
