aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/msg.c')
-rw-r--r--ipc/msg.c239
1 files changed, 118 insertions, 121 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index 46585a05473e..32494e8cc7a5 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -27,6 +27,7 @@
27#include <linux/msg.h> 27#include <linux/msg.h>
28#include <linux/spinlock.h> 28#include <linux/spinlock.h>
29#include <linux/init.h> 29#include <linux/init.h>
30#include <linux/mm.h>
30#include <linux/proc_fs.h> 31#include <linux/proc_fs.h>
31#include <linux/list.h> 32#include <linux/list.h>
32#include <linux/security.h> 33#include <linux/security.h>
@@ -70,7 +71,6 @@ struct msg_sender {
70#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) 71#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS])
71 72
72#define msg_unlock(msq) ipc_unlock(&(msq)->q_perm) 73#define msg_unlock(msq) ipc_unlock(&(msq)->q_perm)
73#define msg_buildid(id, seq) ipc_buildid(id, seq)
74 74
75static void freeque(struct ipc_namespace *, struct kern_ipc_perm *); 75static void freeque(struct ipc_namespace *, struct kern_ipc_perm *);
76static int newque(struct ipc_namespace *, struct ipc_params *); 76static int newque(struct ipc_namespace *, struct ipc_params *);
@@ -78,11 +78,49 @@ static int newque(struct ipc_namespace *, struct ipc_params *);
78static int sysvipc_msg_proc_show(struct seq_file *s, void *it); 78static int sysvipc_msg_proc_show(struct seq_file *s, void *it);
79#endif 79#endif
80 80
81/*
82 * Scale msgmni with the available lowmem size: the memory dedicated to msg
83 * queues should occupy at most 1/MSG_MEM_SCALE of lowmem.
84 * Also take into account the number of nsproxies created so far.
85 * This should be done staying within the (MSGMNI , IPCMNI/nr_ipc_ns) range.
86 */
87void recompute_msgmni(struct ipc_namespace *ns)
88{
89 struct sysinfo i;
90 unsigned long allowed;
91 int nb_ns;
92
93 si_meminfo(&i);
94 allowed = (((i.totalram - i.totalhigh) / MSG_MEM_SCALE) * i.mem_unit)
95 / MSGMNB;
96 nb_ns = atomic_read(&nr_ipc_ns);
97 allowed /= nb_ns;
98
99 if (allowed < MSGMNI) {
100 ns->msg_ctlmni = MSGMNI;
101 goto out_callback;
102 }
103
104 if (allowed > IPCMNI / nb_ns) {
105 ns->msg_ctlmni = IPCMNI / nb_ns;
106 goto out_callback;
107 }
108
109 ns->msg_ctlmni = allowed;
110
111out_callback:
112
113 printk(KERN_INFO "msgmni has been set to %d for ipc namespace %p\n",
114 ns->msg_ctlmni, ns);
115}
116
81void msg_init_ns(struct ipc_namespace *ns) 117void msg_init_ns(struct ipc_namespace *ns)
82{ 118{
83 ns->msg_ctlmax = MSGMAX; 119 ns->msg_ctlmax = MSGMAX;
84 ns->msg_ctlmnb = MSGMNB; 120 ns->msg_ctlmnb = MSGMNB;
85 ns->msg_ctlmni = MSGMNI; 121
122 recompute_msgmni(ns);
123
86 atomic_set(&ns->msg_bytes, 0); 124 atomic_set(&ns->msg_bytes, 0);
87 atomic_set(&ns->msg_hdrs, 0); 125 atomic_set(&ns->msg_hdrs, 0);
88 ipc_init_ids(&ns->ids[IPC_MSG_IDS]); 126 ipc_init_ids(&ns->ids[IPC_MSG_IDS]);
@@ -104,21 +142,6 @@ void __init msg_init(void)
104} 142}
105 143
106/* 144/*
107 * This routine is called in the paths where the rw_mutex is held to protect
108 * access to the idr tree.
109 */
110static inline struct msg_queue *msg_lock_check_down(struct ipc_namespace *ns,
111 int id)
112{
113 struct kern_ipc_perm *ipcp = ipc_lock_check_down(&msg_ids(ns), id);
114
115 if (IS_ERR(ipcp))
116 return (struct msg_queue *)ipcp;
117
118 return container_of(ipcp, struct msg_queue, q_perm);
119}
120
121/*
122 * msg_lock_(check_) routines are called in the paths where the rw_mutex 145 * msg_lock_(check_) routines are called in the paths where the rw_mutex
123 * is not held. 146 * is not held.
124 */ 147 */
@@ -186,7 +209,6 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
186 return id; 209 return id;
187 } 210 }
188 211
189 msq->q_perm.id = msg_buildid(id, msq->q_perm.seq);
190 msq->q_stime = msq->q_rtime = 0; 212 msq->q_stime = msq->q_rtime = 0;
191 msq->q_ctime = get_seconds(); 213 msq->q_ctime = get_seconds();
192 msq->q_cbytes = msq->q_qnum = 0; 214 msq->q_cbytes = msq->q_qnum = 0;
@@ -324,19 +346,19 @@ copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
324 out.msg_rtime = in->msg_rtime; 346 out.msg_rtime = in->msg_rtime;
325 out.msg_ctime = in->msg_ctime; 347 out.msg_ctime = in->msg_ctime;
326 348
327 if (in->msg_cbytes > USHRT_MAX) 349 if (in->msg_cbytes > USHORT_MAX)
328 out.msg_cbytes = USHRT_MAX; 350 out.msg_cbytes = USHORT_MAX;
329 else 351 else
330 out.msg_cbytes = in->msg_cbytes; 352 out.msg_cbytes = in->msg_cbytes;
331 out.msg_lcbytes = in->msg_cbytes; 353 out.msg_lcbytes = in->msg_cbytes;
332 354
333 if (in->msg_qnum > USHRT_MAX) 355 if (in->msg_qnum > USHORT_MAX)
334 out.msg_qnum = USHRT_MAX; 356 out.msg_qnum = USHORT_MAX;
335 else 357 else
336 out.msg_qnum = in->msg_qnum; 358 out.msg_qnum = in->msg_qnum;
337 359
338 if (in->msg_qbytes > USHRT_MAX) 360 if (in->msg_qbytes > USHORT_MAX)
339 out.msg_qbytes = USHRT_MAX; 361 out.msg_qbytes = USHORT_MAX;
340 else 362 else
341 out.msg_qbytes = in->msg_qbytes; 363 out.msg_qbytes = in->msg_qbytes;
342 out.msg_lqbytes = in->msg_qbytes; 364 out.msg_lqbytes = in->msg_qbytes;
@@ -351,31 +373,14 @@ copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
351 } 373 }
352} 374}
353 375
354struct msq_setbuf {
355 unsigned long qbytes;
356 uid_t uid;
357 gid_t gid;
358 mode_t mode;
359};
360
361static inline unsigned long 376static inline unsigned long
362copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version) 377copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version)
363{ 378{
364 switch(version) { 379 switch(version) {
365 case IPC_64: 380 case IPC_64:
366 { 381 if (copy_from_user(out, buf, sizeof(*out)))
367 struct msqid64_ds tbuf;
368
369 if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
370 return -EFAULT; 382 return -EFAULT;
371
372 out->qbytes = tbuf.msg_qbytes;
373 out->uid = tbuf.msg_perm.uid;
374 out->gid = tbuf.msg_perm.gid;
375 out->mode = tbuf.msg_perm.mode;
376
377 return 0; 383 return 0;
378 }
379 case IPC_OLD: 384 case IPC_OLD:
380 { 385 {
381 struct msqid_ds tbuf_old; 386 struct msqid_ds tbuf_old;
@@ -383,14 +388,14 @@ copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
383 if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 388 if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
384 return -EFAULT; 389 return -EFAULT;
385 390
386 out->uid = tbuf_old.msg_perm.uid; 391 out->msg_perm.uid = tbuf_old.msg_perm.uid;
387 out->gid = tbuf_old.msg_perm.gid; 392 out->msg_perm.gid = tbuf_old.msg_perm.gid;
388 out->mode = tbuf_old.msg_perm.mode; 393 out->msg_perm.mode = tbuf_old.msg_perm.mode;
389 394
390 if (tbuf_old.msg_qbytes == 0) 395 if (tbuf_old.msg_qbytes == 0)
391 out->qbytes = tbuf_old.msg_lqbytes; 396 out->msg_qbytes = tbuf_old.msg_lqbytes;
392 else 397 else
393 out->qbytes = tbuf_old.msg_qbytes; 398 out->msg_qbytes = tbuf_old.msg_qbytes;
394 399
395 return 0; 400 return 0;
396 } 401 }
@@ -399,10 +404,71 @@ copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
399 } 404 }
400} 405}
401 406
402asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) 407/*
408 * This function handles some msgctl commands which require the rw_mutex
409 * to be held in write mode.
410 * NOTE: no locks must be held, the rw_mutex is taken inside this function.
411 */
412static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
413 struct msqid_ds __user *buf, int version)
403{ 414{
404 struct kern_ipc_perm *ipcp; 415 struct kern_ipc_perm *ipcp;
405 struct msq_setbuf uninitialized_var(setbuf); 416 struct msqid64_ds msqid64;
417 struct msg_queue *msq;
418 int err;
419
420 if (cmd == IPC_SET) {
421 if (copy_msqid_from_user(&msqid64, buf, version))
422 return -EFAULT;
423 }
424
425 ipcp = ipcctl_pre_down(&msg_ids(ns), msqid, cmd,
426 &msqid64.msg_perm, msqid64.msg_qbytes);
427 if (IS_ERR(ipcp))
428 return PTR_ERR(ipcp);
429
430 msq = container_of(ipcp, struct msg_queue, q_perm);
431
432 err = security_msg_queue_msgctl(msq, cmd);
433 if (err)
434 goto out_unlock;
435
436 switch (cmd) {
437 case IPC_RMID:
438 freeque(ns, ipcp);
439 goto out_up;
440 case IPC_SET:
441 if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
442 !capable(CAP_SYS_RESOURCE)) {
443 err = -EPERM;
444 goto out_unlock;
445 }
446
447 msq->q_qbytes = msqid64.msg_qbytes;
448
449 ipc_update_perm(&msqid64.msg_perm, ipcp);
450 msq->q_ctime = get_seconds();
451 /* sleeping receivers might be excluded by
452 * stricter permissions.
453 */
454 expunge_all(msq, -EAGAIN);
455 /* sleeping senders might be able to send
456 * due to a larger queue size.
457 */
458 ss_wakeup(&msq->q_senders, 0);
459 break;
460 default:
461 err = -EINVAL;
462 }
463out_unlock:
464 msg_unlock(msq);
465out_up:
466 up_write(&msg_ids(ns).rw_mutex);
467 return err;
468}
469
470asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
471{
406 struct msg_queue *msq; 472 struct msg_queue *msq;
407 int err, version; 473 int err, version;
408 struct ipc_namespace *ns; 474 struct ipc_namespace *ns;
@@ -498,82 +564,13 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
498 return success_return; 564 return success_return;
499 } 565 }
500 case IPC_SET: 566 case IPC_SET:
501 if (!buf)
502 return -EFAULT;
503 if (copy_msqid_from_user(&setbuf, buf, version))
504 return -EFAULT;
505 break;
506 case IPC_RMID: 567 case IPC_RMID:
507 break; 568 err = msgctl_down(ns, msqid, cmd, buf, version);
569 return err;
508 default: 570 default:
509 return -EINVAL; 571 return -EINVAL;
510 } 572 }
511 573
512 down_write(&msg_ids(ns).rw_mutex);
513 msq = msg_lock_check_down(ns, msqid);
514 if (IS_ERR(msq)) {
515 err = PTR_ERR(msq);
516 goto out_up;
517 }
518
519 ipcp = &msq->q_perm;
520
521 err = audit_ipc_obj(ipcp);
522 if (err)
523 goto out_unlock_up;
524 if (cmd == IPC_SET) {
525 err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
526 setbuf.mode);
527 if (err)
528 goto out_unlock_up;
529 }
530
531 err = -EPERM;
532 if (current->euid != ipcp->cuid &&
533 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
534 /* We _could_ check for CAP_CHOWN above, but we don't */
535 goto out_unlock_up;
536
537 err = security_msg_queue_msgctl(msq, cmd);
538 if (err)
539 goto out_unlock_up;
540
541 switch (cmd) {
542 case IPC_SET:
543 {
544 err = -EPERM;
545 if (setbuf.qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
546 goto out_unlock_up;
547
548 msq->q_qbytes = setbuf.qbytes;
549
550 ipcp->uid = setbuf.uid;
551 ipcp->gid = setbuf.gid;
552 ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
553 (S_IRWXUGO & setbuf.mode);
554 msq->q_ctime = get_seconds();
555 /* sleeping receivers might be excluded by
556 * stricter permissions.
557 */
558 expunge_all(msq, -EAGAIN);
559 /* sleeping senders might be able to send
560 * due to a larger queue size.
561 */
562 ss_wakeup(&msq->q_senders, 0);
563 msg_unlock(msq);
564 break;
565 }
566 case IPC_RMID:
567 freeque(ns, &msq->q_perm);
568 break;
569 }
570 err = 0;
571out_up:
572 up_write(&msg_ids(ns).rw_mutex);
573 return err;
574out_unlock_up:
575 msg_unlock(msq);
576 goto out_up;
577out_unlock: 574out_unlock:
578 msg_unlock(msq); 575 msg_unlock(msq);
579 return err; 576 return err;