msgctl(): split the actual work from copyin/copyout

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 156d9ed1 553f770e

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