diff options
Diffstat (limited to 'ipc/msg.c')
-rw-r--r-- | ipc/msg.c | 239 |
1 files changed, 118 insertions, 121 deletions
@@ -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 | ||
75 | static void freeque(struct ipc_namespace *, struct kern_ipc_perm *); | 75 | static void freeque(struct ipc_namespace *, struct kern_ipc_perm *); |
76 | static int newque(struct ipc_namespace *, struct ipc_params *); | 76 | static int newque(struct ipc_namespace *, struct ipc_params *); |
@@ -78,11 +78,49 @@ static int newque(struct ipc_namespace *, struct ipc_params *); | |||
78 | static int sysvipc_msg_proc_show(struct seq_file *s, void *it); | 78 | static 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 | */ | ||
87 | void 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 | |||
111 | out_callback: | ||
112 | |||
113 | printk(KERN_INFO "msgmni has been set to %d for ipc namespace %p\n", | ||
114 | ns->msg_ctlmni, ns); | ||
115 | } | ||
116 | |||
81 | void msg_init_ns(struct ipc_namespace *ns) | 117 | void 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 | */ | ||
110 | static 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 | ||
354 | struct msq_setbuf { | ||
355 | unsigned long qbytes; | ||
356 | uid_t uid; | ||
357 | gid_t gid; | ||
358 | mode_t mode; | ||
359 | }; | ||
360 | |||
361 | static inline unsigned long | 376 | static inline unsigned long |
362 | copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version) | 377 | copy_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 | ||
402 | asmlinkage 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 | */ | ||
412 | static 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 | } | ||
463 | out_unlock: | ||
464 | msg_unlock(msq); | ||
465 | out_up: | ||
466 | up_write(&msg_ids(ns).rw_mutex); | ||
467 | return err; | ||
468 | } | ||
469 | |||
470 | asmlinkage 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; | ||
571 | out_up: | ||
572 | up_write(&msg_ids(ns).rw_mutex); | ||
573 | return err; | ||
574 | out_unlock_up: | ||
575 | msg_unlock(msq); | ||
576 | goto out_up; | ||
577 | out_unlock: | 574 | out_unlock: |
578 | msg_unlock(msq); | 575 | msg_unlock(msq); |
579 | return err; | 576 | return err; |