diff options
Diffstat (limited to 'ipc/msg.c')
-rw-r--r-- | ipc/msg.c | 202 |
1 files changed, 96 insertions, 106 deletions
@@ -361,23 +361,17 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) | |||
361 | * NOTE: no locks must be held, the rwsem is taken inside this function. | 361 | * NOTE: no locks must be held, the rwsem is taken inside this function. |
362 | */ | 362 | */ |
363 | static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | 363 | static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, |
364 | struct msqid_ds __user *buf, int version) | 364 | struct msqid64_ds *msqid64) |
365 | { | 365 | { |
366 | struct kern_ipc_perm *ipcp; | 366 | struct kern_ipc_perm *ipcp; |
367 | struct msqid64_ds uninitialized_var(msqid64); | ||
368 | struct msg_queue *msq; | 367 | struct msg_queue *msq; |
369 | int err; | 368 | int err; |
370 | 369 | ||
371 | if (cmd == IPC_SET) { | ||
372 | if (copy_msqid_from_user(&msqid64, buf, version)) | ||
373 | return -EFAULT; | ||
374 | } | ||
375 | |||
376 | down_write(&msg_ids(ns).rwsem); | 370 | down_write(&msg_ids(ns).rwsem); |
377 | rcu_read_lock(); | 371 | rcu_read_lock(); |
378 | 372 | ||
379 | ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd, | 373 | ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd, |
380 | &msqid64.msg_perm, msqid64.msg_qbytes); | 374 | &msqid64->msg_perm, msqid64->msg_qbytes); |
381 | if (IS_ERR(ipcp)) { | 375 | if (IS_ERR(ipcp)) { |
382 | err = PTR_ERR(ipcp); | 376 | err = PTR_ERR(ipcp); |
383 | goto out_unlock1; | 377 | goto out_unlock1; |
@@ -399,18 +393,18 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | |||
399 | { | 393 | { |
400 | DEFINE_WAKE_Q(wake_q); | 394 | DEFINE_WAKE_Q(wake_q); |
401 | 395 | ||
402 | if (msqid64.msg_qbytes > ns->msg_ctlmnb && | 396 | if (msqid64->msg_qbytes > ns->msg_ctlmnb && |
403 | !capable(CAP_SYS_RESOURCE)) { | 397 | !capable(CAP_SYS_RESOURCE)) { |
404 | err = -EPERM; | 398 | err = -EPERM; |
405 | goto out_unlock1; | 399 | goto out_unlock1; |
406 | } | 400 | } |
407 | 401 | ||
408 | ipc_lock_object(&msq->q_perm); | 402 | ipc_lock_object(&msq->q_perm); |
409 | err = ipc_update_perm(&msqid64.msg_perm, ipcp); | 403 | err = ipc_update_perm(&msqid64->msg_perm, ipcp); |
410 | if (err) | 404 | if (err) |
411 | goto out_unlock0; | 405 | goto out_unlock0; |
412 | 406 | ||
413 | msq->q_qbytes = msqid64.msg_qbytes; | 407 | msq->q_qbytes = msqid64->msg_qbytes; |
414 | 408 | ||
415 | msq->q_ctime = get_seconds(); | 409 | msq->q_ctime = get_seconds(); |
416 | /* | 410 | /* |
@@ -442,111 +436,89 @@ out_up: | |||
442 | return err; | 436 | return err; |
443 | } | 437 | } |
444 | 438 | ||
445 | static int msgctl_nolock(struct ipc_namespace *ns, int msqid, | 439 | static int msgctl_info(struct ipc_namespace *ns, int msqid, |
446 | int cmd, int version, void __user *buf) | 440 | int cmd, struct msginfo *msginfo) |
447 | { | 441 | { |
448 | int err; | 442 | int err; |
449 | struct msg_queue *msq; | 443 | int max_id; |
450 | |||
451 | switch (cmd) { | ||
452 | case IPC_INFO: | ||
453 | case MSG_INFO: | ||
454 | { | ||
455 | struct msginfo msginfo; | ||
456 | int max_id; | ||
457 | |||
458 | if (!buf) | ||
459 | return -EFAULT; | ||
460 | 444 | ||
461 | /* | 445 | /* |
462 | * We must not return kernel stack data. | 446 | * We must not return kernel stack data. |
463 | * due to padding, it's not enough | 447 | * due to padding, it's not enough |
464 | * to set all member fields. | 448 | * to set all member fields. |
465 | */ | 449 | */ |
466 | err = security_msg_queue_msgctl(NULL, cmd); | 450 | err = security_msg_queue_msgctl(NULL, cmd); |
467 | if (err) | 451 | if (err) |
468 | return err; | 452 | return err; |
469 | 453 | ||
470 | memset(&msginfo, 0, sizeof(msginfo)); | 454 | memset(msginfo, 0, sizeof(*msginfo)); |
471 | msginfo.msgmni = ns->msg_ctlmni; | 455 | msginfo->msgmni = ns->msg_ctlmni; |
472 | msginfo.msgmax = ns->msg_ctlmax; | 456 | msginfo->msgmax = ns->msg_ctlmax; |
473 | msginfo.msgmnb = ns->msg_ctlmnb; | 457 | msginfo->msgmnb = ns->msg_ctlmnb; |
474 | msginfo.msgssz = MSGSSZ; | 458 | msginfo->msgssz = MSGSSZ; |
475 | msginfo.msgseg = MSGSEG; | 459 | msginfo->msgseg = MSGSEG; |
476 | down_read(&msg_ids(ns).rwsem); | 460 | down_read(&msg_ids(ns).rwsem); |
477 | if (cmd == MSG_INFO) { | 461 | if (cmd == MSG_INFO) { |
478 | msginfo.msgpool = msg_ids(ns).in_use; | 462 | msginfo->msgpool = msg_ids(ns).in_use; |
479 | msginfo.msgmap = atomic_read(&ns->msg_hdrs); | 463 | msginfo->msgmap = atomic_read(&ns->msg_hdrs); |
480 | msginfo.msgtql = atomic_read(&ns->msg_bytes); | 464 | msginfo->msgtql = atomic_read(&ns->msg_bytes); |
481 | } else { | 465 | } else { |
482 | msginfo.msgmap = MSGMAP; | 466 | msginfo->msgmap = MSGMAP; |
483 | msginfo.msgpool = MSGPOOL; | 467 | msginfo->msgpool = MSGPOOL; |
484 | msginfo.msgtql = MSGTQL; | 468 | msginfo->msgtql = MSGTQL; |
485 | } | ||
486 | max_id = ipc_get_maxid(&msg_ids(ns)); | ||
487 | up_read(&msg_ids(ns).rwsem); | ||
488 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) | ||
489 | return -EFAULT; | ||
490 | return (max_id < 0) ? 0 : max_id; | ||
491 | } | 469 | } |
470 | max_id = ipc_get_maxid(&msg_ids(ns)); | ||
471 | up_read(&msg_ids(ns).rwsem); | ||
472 | return (max_id < 0) ? 0 : max_id; | ||
473 | } | ||
492 | 474 | ||
493 | case MSG_STAT: | 475 | static int msgctl_stat(struct ipc_namespace *ns, int msqid, |
494 | case IPC_STAT: | 476 | int cmd, struct msqid64_ds *p) |
495 | { | 477 | { |
496 | struct msqid64_ds tbuf; | 478 | int err; |
497 | int success_return; | 479 | struct msg_queue *msq; |
498 | 480 | int success_return; | |
499 | if (!buf) | ||
500 | return -EFAULT; | ||
501 | |||
502 | memset(&tbuf, 0, sizeof(tbuf)); | ||
503 | 481 | ||
504 | rcu_read_lock(); | 482 | memset(p, 0, sizeof(*p)); |
505 | if (cmd == MSG_STAT) { | ||
506 | msq = msq_obtain_object(ns, msqid); | ||
507 | if (IS_ERR(msq)) { | ||
508 | err = PTR_ERR(msq); | ||
509 | goto out_unlock; | ||
510 | } | ||
511 | success_return = msq->q_perm.id; | ||
512 | } else { | ||
513 | msq = msq_obtain_object_check(ns, msqid); | ||
514 | if (IS_ERR(msq)) { | ||
515 | err = PTR_ERR(msq); | ||
516 | goto out_unlock; | ||
517 | } | ||
518 | success_return = 0; | ||
519 | } | ||
520 | 483 | ||
521 | err = -EACCES; | 484 | rcu_read_lock(); |
522 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) | 485 | if (cmd == MSG_STAT) { |
486 | msq = msq_obtain_object(ns, msqid); | ||
487 | if (IS_ERR(msq)) { | ||
488 | err = PTR_ERR(msq); | ||
523 | goto out_unlock; | 489 | goto out_unlock; |
524 | 490 | } | |
525 | err = security_msg_queue_msgctl(msq, cmd); | 491 | success_return = msq->q_perm.id; |
526 | if (err) | 492 | } else { |
493 | msq = msq_obtain_object_check(ns, msqid); | ||
494 | if (IS_ERR(msq)) { | ||
495 | err = PTR_ERR(msq); | ||
527 | goto out_unlock; | 496 | goto out_unlock; |
497 | } | ||
498 | success_return = 0; | ||
499 | } | ||
528 | 500 | ||
529 | kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm); | 501 | err = -EACCES; |
530 | tbuf.msg_stime = msq->q_stime; | 502 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) |
531 | tbuf.msg_rtime = msq->q_rtime; | 503 | goto out_unlock; |
532 | tbuf.msg_ctime = msq->q_ctime; | ||
533 | tbuf.msg_cbytes = msq->q_cbytes; | ||
534 | tbuf.msg_qnum = msq->q_qnum; | ||
535 | tbuf.msg_qbytes = msq->q_qbytes; | ||
536 | tbuf.msg_lspid = msq->q_lspid; | ||
537 | tbuf.msg_lrpid = msq->q_lrpid; | ||
538 | rcu_read_unlock(); | ||
539 | 504 | ||
540 | if (copy_msqid_to_user(buf, &tbuf, version)) | 505 | err = security_msg_queue_msgctl(msq, cmd); |
541 | return -EFAULT; | 506 | if (err) |
542 | return success_return; | 507 | goto out_unlock; |
543 | } | 508 | |
509 | kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm); | ||
510 | p->msg_stime = msq->q_stime; | ||
511 | p->msg_rtime = msq->q_rtime; | ||
512 | p->msg_ctime = msq->q_ctime; | ||
513 | p->msg_cbytes = msq->q_cbytes; | ||
514 | p->msg_qnum = msq->q_qnum; | ||
515 | p->msg_qbytes = msq->q_qbytes; | ||
516 | p->msg_lspid = msq->q_lspid; | ||
517 | p->msg_lrpid = msq->q_lrpid; | ||
518 | rcu_read_unlock(); | ||
544 | 519 | ||
545 | default: | 520 | return success_return; |
546 | return -EINVAL; | ||
547 | } | ||
548 | 521 | ||
549 | return err; | ||
550 | out_unlock: | 522 | out_unlock: |
551 | rcu_read_unlock(); | 523 | rcu_read_unlock(); |
552 | return err; | 524 | return err; |
@@ -556,6 +528,8 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) | |||
556 | { | 528 | { |
557 | int version; | 529 | int version; |
558 | struct ipc_namespace *ns; | 530 | struct ipc_namespace *ns; |
531 | struct msqid64_ds msqid64; | ||
532 | int err; | ||
559 | 533 | ||
560 | if (msqid < 0 || cmd < 0) | 534 | if (msqid < 0 || cmd < 0) |
561 | return -EINVAL; | 535 | return -EINVAL; |
@@ -565,13 +539,29 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) | |||
565 | 539 | ||
566 | switch (cmd) { | 540 | switch (cmd) { |
567 | case IPC_INFO: | 541 | case IPC_INFO: |
568 | case MSG_INFO: | 542 | case MSG_INFO: { |
543 | struct msginfo msginfo; | ||
544 | err = msgctl_info(ns, msqid, cmd, &msginfo); | ||
545 | if (err < 0) | ||
546 | return err; | ||
547 | if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) | ||
548 | err = -EFAULT; | ||
549 | return err; | ||
550 | } | ||
569 | case MSG_STAT: /* msqid is an index rather than a msg queue id */ | 551 | case MSG_STAT: /* msqid is an index rather than a msg queue id */ |
570 | case IPC_STAT: | 552 | case IPC_STAT: |
571 | return msgctl_nolock(ns, msqid, cmd, version, buf); | 553 | err = msgctl_stat(ns, msqid, cmd, &msqid64); |
554 | if (err < 0) | ||
555 | return err; | ||
556 | if (copy_msqid_to_user(buf, &msqid64, version)) | ||
557 | err = -EFAULT; | ||
558 | return err; | ||
572 | case IPC_SET: | 559 | case IPC_SET: |
560 | if (copy_msqid_from_user(&msqid64, buf, version)) | ||
561 | return -EFAULT; | ||
562 | /* fallthru */ | ||
573 | case IPC_RMID: | 563 | case IPC_RMID: |
574 | return msgctl_down(ns, msqid, cmd, buf, version); | 564 | return msgctl_down(ns, msqid, cmd, &msqid64); |
575 | default: | 565 | default: |
576 | return -EINVAL; | 566 | return -EINVAL; |
577 | } | 567 | } |