.. | .. |
---|
61 | 61 | struct list_head q_senders; |
---|
62 | 62 | } __randomize_layout; |
---|
63 | 63 | |
---|
| 64 | +/* |
---|
| 65 | + * MSG_BARRIER Locking: |
---|
| 66 | + * |
---|
| 67 | + * Similar to the optimization used in ipc/mqueue.c, one syscall return path |
---|
| 68 | + * does not acquire any locks when it sees that a message exists in |
---|
| 69 | + * msg_receiver.r_msg. Therefore r_msg is set using smp_store_release() |
---|
| 70 | + * and accessed using READ_ONCE()+smp_acquire__after_ctrl_dep(). In addition, |
---|
| 71 | + * wake_q_add_safe() is used. See ipc/mqueue.c for more details |
---|
| 72 | + */ |
---|
| 73 | + |
---|
64 | 74 | /* one msg_receiver structure for each sleeping receiver */ |
---|
65 | 75 | struct msg_receiver { |
---|
66 | 76 | struct list_head r_list; |
---|
.. | .. |
---|
137 | 147 | key_t key = params->key; |
---|
138 | 148 | int msgflg = params->flg; |
---|
139 | 149 | |
---|
140 | | - msq = kvmalloc(sizeof(*msq), GFP_KERNEL); |
---|
| 150 | + msq = kvmalloc(sizeof(*msq), GFP_KERNEL_ACCOUNT); |
---|
141 | 151 | if (unlikely(!msq)) |
---|
142 | 152 | return -ENOMEM; |
---|
143 | 153 | |
---|
.. | .. |
---|
184 | 194 | { |
---|
185 | 195 | mss->tsk = current; |
---|
186 | 196 | mss->msgsz = msgsz; |
---|
| 197 | + /* |
---|
| 198 | + * No memory barrier required: we did ipc_lock_object(), |
---|
| 199 | + * and the waker obtains that lock before calling wake_q_add(). |
---|
| 200 | + */ |
---|
187 | 201 | __set_current_state(TASK_INTERRUPTIBLE); |
---|
188 | 202 | list_add_tail(&mss->list, &msq->q_senders); |
---|
189 | 203 | } |
---|
.. | .. |
---|
237 | 251 | struct msg_receiver *msr, *t; |
---|
238 | 252 | |
---|
239 | 253 | list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) { |
---|
240 | | - wake_q_add(wake_q, msr->r_tsk); |
---|
241 | | - WRITE_ONCE(msr->r_msg, ERR_PTR(res)); |
---|
| 254 | + struct task_struct *r_tsk; |
---|
| 255 | + |
---|
| 256 | + r_tsk = get_task_struct(msr->r_tsk); |
---|
| 257 | + |
---|
| 258 | + /* see MSG_BARRIER for purpose/pairing */ |
---|
| 259 | + smp_store_release(&msr->r_msg, ERR_PTR(res)); |
---|
| 260 | + wake_q_add_safe(wake_q, r_tsk); |
---|
242 | 261 | } |
---|
243 | 262 | } |
---|
244 | 263 | |
---|
.. | .. |
---|
251 | 270 | * before freeque() is called. msg_ids.rwsem remains locked on exit. |
---|
252 | 271 | */ |
---|
253 | 272 | static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) |
---|
| 273 | + __releases(RCU) |
---|
| 274 | + __releases(&msq->q_perm) |
---|
254 | 275 | { |
---|
255 | 276 | struct msg_msg *msg, *t; |
---|
256 | 277 | struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); |
---|
.. | .. |
---|
567 | 588 | return err; |
---|
568 | 589 | } |
---|
569 | 590 | |
---|
570 | | -long ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) |
---|
| 591 | +static long ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf, int version) |
---|
571 | 592 | { |
---|
572 | | - int version; |
---|
573 | 593 | struct ipc_namespace *ns; |
---|
574 | 594 | struct msqid64_ds msqid64; |
---|
575 | 595 | int err; |
---|
.. | .. |
---|
577 | 597 | if (msqid < 0 || cmd < 0) |
---|
578 | 598 | return -EINVAL; |
---|
579 | 599 | |
---|
580 | | - version = ipc_parse_version(&cmd); |
---|
581 | 600 | ns = current->nsproxy->ipc_ns; |
---|
582 | 601 | |
---|
583 | 602 | switch (cmd) { |
---|
.. | .. |
---|
614 | 633 | |
---|
615 | 634 | SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) |
---|
616 | 635 | { |
---|
617 | | - return ksys_msgctl(msqid, cmd, buf); |
---|
| 636 | + return ksys_msgctl(msqid, cmd, buf, IPC_64); |
---|
618 | 637 | } |
---|
| 638 | + |
---|
| 639 | +#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION |
---|
| 640 | +long ksys_old_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) |
---|
| 641 | +{ |
---|
| 642 | + int version = ipc_parse_version(&cmd); |
---|
| 643 | + |
---|
| 644 | + return ksys_msgctl(msqid, cmd, buf, version); |
---|
| 645 | +} |
---|
| 646 | + |
---|
| 647 | +SYSCALL_DEFINE3(old_msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) |
---|
| 648 | +{ |
---|
| 649 | + return ksys_old_msgctl(msqid, cmd, buf); |
---|
| 650 | +} |
---|
| 651 | +#endif |
---|
619 | 652 | |
---|
620 | 653 | #ifdef CONFIG_COMPAT |
---|
621 | 654 | |
---|
.. | .. |
---|
623 | 656 | struct compat_ipc_perm msg_perm; |
---|
624 | 657 | compat_uptr_t msg_first; |
---|
625 | 658 | compat_uptr_t msg_last; |
---|
626 | | - compat_time_t msg_stime; |
---|
627 | | - compat_time_t msg_rtime; |
---|
628 | | - compat_time_t msg_ctime; |
---|
| 659 | + old_time32_t msg_stime; |
---|
| 660 | + old_time32_t msg_rtime; |
---|
| 661 | + old_time32_t msg_ctime; |
---|
629 | 662 | compat_ulong_t msg_lcbytes; |
---|
630 | 663 | compat_ulong_t msg_lqbytes; |
---|
631 | 664 | unsigned short msg_cbytes; |
---|
.. | .. |
---|
690 | 723 | } |
---|
691 | 724 | } |
---|
692 | 725 | |
---|
693 | | -long compat_ksys_msgctl(int msqid, int cmd, void __user *uptr) |
---|
| 726 | +static long compat_ksys_msgctl(int msqid, int cmd, void __user *uptr, int version) |
---|
694 | 727 | { |
---|
695 | 728 | struct ipc_namespace *ns; |
---|
696 | 729 | int err; |
---|
697 | 730 | struct msqid64_ds msqid64; |
---|
698 | | - int version = compat_ipc_parse_version(&cmd); |
---|
699 | 731 | |
---|
700 | 732 | ns = current->nsproxy->ipc_ns; |
---|
701 | 733 | |
---|
.. | .. |
---|
735 | 767 | |
---|
736 | 768 | COMPAT_SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, void __user *, uptr) |
---|
737 | 769 | { |
---|
738 | | - return compat_ksys_msgctl(msqid, cmd, uptr); |
---|
| 770 | + return compat_ksys_msgctl(msqid, cmd, uptr, IPC_64); |
---|
739 | 771 | } |
---|
| 772 | + |
---|
| 773 | +#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION |
---|
| 774 | +long compat_ksys_old_msgctl(int msqid, int cmd, void __user *uptr) |
---|
| 775 | +{ |
---|
| 776 | + int version = compat_ipc_parse_version(&cmd); |
---|
| 777 | + |
---|
| 778 | + return compat_ksys_msgctl(msqid, cmd, uptr, version); |
---|
| 779 | +} |
---|
| 780 | + |
---|
| 781 | +COMPAT_SYSCALL_DEFINE3(old_msgctl, int, msqid, int, cmd, void __user *, uptr) |
---|
| 782 | +{ |
---|
| 783 | + return compat_ksys_old_msgctl(msqid, cmd, uptr); |
---|
| 784 | +} |
---|
| 785 | +#endif |
---|
740 | 786 | #endif |
---|
741 | 787 | |
---|
742 | 788 | static int testmsg(struct msg_msg *msg, long type, int mode) |
---|
.. | .. |
---|
774 | 820 | list_del(&msr->r_list); |
---|
775 | 821 | if (msr->r_maxsize < msg->m_ts) { |
---|
776 | 822 | wake_q_add(wake_q, msr->r_tsk); |
---|
777 | | - WRITE_ONCE(msr->r_msg, ERR_PTR(-E2BIG)); |
---|
| 823 | + |
---|
| 824 | + /* See expunge_all regarding memory barrier */ |
---|
| 825 | + smp_store_release(&msr->r_msg, ERR_PTR(-E2BIG)); |
---|
778 | 826 | } else { |
---|
779 | 827 | ipc_update_pid(&msq->q_lrpid, task_pid(msr->r_tsk)); |
---|
780 | 828 | msq->q_rtime = ktime_get_real_seconds(); |
---|
781 | 829 | |
---|
782 | 830 | wake_q_add(wake_q, msr->r_tsk); |
---|
783 | | - WRITE_ONCE(msr->r_msg, msg); |
---|
| 831 | + |
---|
| 832 | + /* See expunge_all regarding memory barrier */ |
---|
| 833 | + smp_store_release(&msr->r_msg, msg); |
---|
784 | 834 | return 1; |
---|
785 | 835 | } |
---|
786 | 836 | } |
---|
.. | .. |
---|
1130 | 1180 | msr_d.r_maxsize = INT_MAX; |
---|
1131 | 1181 | else |
---|
1132 | 1182 | msr_d.r_maxsize = bufsz; |
---|
1133 | | - msr_d.r_msg = ERR_PTR(-EAGAIN); |
---|
| 1183 | + |
---|
| 1184 | + /* memory barrier not require due to ipc_lock_object() */ |
---|
| 1185 | + WRITE_ONCE(msr_d.r_msg, ERR_PTR(-EAGAIN)); |
---|
| 1186 | + |
---|
| 1187 | + /* memory barrier not required, we own ipc_lock_object() */ |
---|
1134 | 1188 | __set_current_state(TASK_INTERRUPTIBLE); |
---|
1135 | 1189 | |
---|
1136 | 1190 | ipc_unlock_object(&msq->q_perm); |
---|
.. | .. |
---|
1159 | 1213 | * signal) it will either see the message and continue ... |
---|
1160 | 1214 | */ |
---|
1161 | 1215 | msg = READ_ONCE(msr_d.r_msg); |
---|
1162 | | - if (msg != ERR_PTR(-EAGAIN)) |
---|
| 1216 | + if (msg != ERR_PTR(-EAGAIN)) { |
---|
| 1217 | + /* see MSG_BARRIER for purpose/pairing */ |
---|
| 1218 | + smp_acquire__after_ctrl_dep(); |
---|
| 1219 | + |
---|
1163 | 1220 | goto out_unlock1; |
---|
| 1221 | + } |
---|
1164 | 1222 | |
---|
1165 | 1223 | /* |
---|
1166 | 1224 | * ... or see -EAGAIN, acquire the lock to check the message |
---|
.. | .. |
---|
1168 | 1226 | */ |
---|
1169 | 1227 | ipc_lock_object(&msq->q_perm); |
---|
1170 | 1228 | |
---|
1171 | | - msg = msr_d.r_msg; |
---|
| 1229 | + msg = READ_ONCE(msr_d.r_msg); |
---|
1172 | 1230 | if (msg != ERR_PTR(-EAGAIN)) |
---|
1173 | 1231 | goto out_unlock0; |
---|
1174 | 1232 | |
---|