diff options
| author | shamir rabinovitch <shamir.rabinovitch@oracle.com> | 2018-12-16 02:01:08 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2018-12-19 13:27:58 -0500 |
| commit | ea010070d0a7497253d5a6f919f6dd107450b31a (patch) | |
| tree | 4bd1b33edd72bdd73d39e7f8160a73730704fa56 | |
| parent | c6f4075e2f14a91f2180c98bc7715946f791cbe6 (diff) | |
net/rds: fix warn in rds_message_alloc_sgs
redundant copy_from_user in rds_sendmsg system call expose rds
to issue where rds_rdma_extra_size walk the rds iovec and and
calculate the number pf pages (sgs) it need to add to the tail of
rds message and later rds_cmsg_rdma_args copy the rds iovec again
and re calculate the same number and get different result causing
WARN_ON in rds_message_alloc_sgs.
fix this by doing the copy_from_user only once per rds_sendmsg
system call.
When issue occur the below dump is seen:
WARNING: CPU: 0 PID: 19789 at net/rds/message.c:316 rds_message_alloc_sgs+0x10c/0x160 net/rds/message.c:316
Kernel panic - not syncing: panic_on_warn set ...
CPU: 0 PID: 19789 Comm: syz-executor827 Not tainted 4.19.0-next-20181030+ #101
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x244/0x39d lib/dump_stack.c:113
panic+0x2ad/0x55c kernel/panic.c:188
__warn.cold.8+0x20/0x45 kernel/panic.c:540
report_bug+0x254/0x2d0 lib/bug.c:186
fixup_bug arch/x86/kernel/traps.c:178 [inline]
do_error_trap+0x11b/0x200 arch/x86/kernel/traps.c:271
do_invalid_op+0x36/0x40 arch/x86/kernel/traps.c:290
invalid_op+0x14/0x20 arch/x86/entry/entry_64.S:969
RIP: 0010:rds_message_alloc_sgs+0x10c/0x160 net/rds/message.c:316
Code: c0 74 04 3c 03 7e 6c 44 01 ab 78 01 00 00 e8 2b 9e 35 fa 4c 89 e0 48 83 c4 08 5b 41 5c 41 5d 41 5e 41 5f 5d c3 e8 14 9e 35 fa <0f> 0b 31 ff 44 89 ee e8 18 9f 35 fa 45 85 ed 75 1b e8 fe 9d 35 fa
RSP: 0018:ffff8801c51b7460 EFLAGS: 00010293
RAX: ffff8801bc412080 RBX: ffff8801d7bf4040 RCX: ffffffff8749c9e6
RDX: 0000000000000000 RSI: ffffffff8749ca5c RDI: 0000000000000004
RBP: ffff8801c51b7490 R08: ffff8801bc412080 R09: ffffed003b5c5b67
R10: ffffed003b5c5b67 R11: ffff8801dae2db3b R12: 0000000000000000
R13: 000000000007165c R14: 000000000007165c R15: 0000000000000005
rds_cmsg_rdma_args+0x82d/0x1510 net/rds/rdma.c:623
rds_cmsg_send net/rds/send.c:971 [inline]
rds_sendmsg+0x19a2/0x3180 net/rds/send.c:1273
sock_sendmsg_nosec net/socket.c:622 [inline]
sock_sendmsg+0xd5/0x120 net/socket.c:632
___sys_sendmsg+0x7fd/0x930 net/socket.c:2117
__sys_sendmsg+0x11d/0x280 net/socket.c:2155
__do_sys_sendmsg net/socket.c:2164 [inline]
__se_sys_sendmsg net/socket.c:2162 [inline]
__x64_sys_sendmsg+0x78/0xb0 net/socket.c:2162
do_syscall_64+0x1b9/0x820 arch/x86/entry/common.c:290
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x44a859
Code: e8 dc e6 ff ff 48 83 c4 18 c3 0f 1f 80 00 00 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 6b cb fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f1d4710ada8 EFLAGS: 00000297 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00000000006dcc28 RCX: 000000000044a859
RDX: 0000000000000000 RSI: 0000000020001600 RDI: 0000000000000003
RBP: 00000000006dcc20 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000297 R12: 00000000006dcc2c
R13: 646e732f7665642f R14: 00007f1d4710b9c0 R15: 00000000006dcd2c
Kernel Offset: disabled
Rebooting in 86400 seconds..
Reported-by: syzbot+26de17458aeda9d305d8@syzkaller.appspotmail.com
Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: shamir rabinovitch <shamir.rabinovitch@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | net/rds/rdma.c | 63 | ||||
| -rw-r--r-- | net/rds/rds.h | 20 | ||||
| -rw-r--r-- | net/rds/send.c | 50 |
3 files changed, 91 insertions, 42 deletions
diff --git a/net/rds/rdma.c b/net/rds/rdma.c index 98237feb607a..e1965d9cbcf8 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c | |||
| @@ -517,9 +517,10 @@ static int rds_rdma_pages(struct rds_iovec iov[], int nr_iovecs) | |||
| 517 | return tot_pages; | 517 | return tot_pages; |
| 518 | } | 518 | } |
| 519 | 519 | ||
| 520 | int rds_rdma_extra_size(struct rds_rdma_args *args) | 520 | int rds_rdma_extra_size(struct rds_rdma_args *args, |
| 521 | struct rds_iov_vector *iov) | ||
| 521 | { | 522 | { |
| 522 | struct rds_iovec vec; | 523 | struct rds_iovec *vec; |
| 523 | struct rds_iovec __user *local_vec; | 524 | struct rds_iovec __user *local_vec; |
| 524 | int tot_pages = 0; | 525 | int tot_pages = 0; |
| 525 | unsigned int nr_pages; | 526 | unsigned int nr_pages; |
| @@ -530,13 +531,23 @@ int rds_rdma_extra_size(struct rds_rdma_args *args) | |||
| 530 | if (args->nr_local == 0) | 531 | if (args->nr_local == 0) |
| 531 | return -EINVAL; | 532 | return -EINVAL; |
| 532 | 533 | ||
| 534 | iov->iov = kcalloc(args->nr_local, | ||
| 535 | sizeof(struct rds_iovec), | ||
| 536 | GFP_KERNEL); | ||
| 537 | if (!iov->iov) | ||
| 538 | return -ENOMEM; | ||
| 539 | |||
| 540 | vec = &iov->iov[0]; | ||
| 541 | |||
| 542 | if (copy_from_user(vec, local_vec, args->nr_local * | ||
| 543 | sizeof(struct rds_iovec))) | ||
| 544 | return -EFAULT; | ||
| 545 | iov->len = args->nr_local; | ||
| 546 | |||
| 533 | /* figure out the number of pages in the vector */ | 547 | /* figure out the number of pages in the vector */ |
| 534 | for (i = 0; i < args->nr_local; i++) { | 548 | for (i = 0; i < args->nr_local; i++, vec++) { |
| 535 | if (copy_from_user(&vec, &local_vec[i], | ||
| 536 | sizeof(struct rds_iovec))) | ||
| 537 | return -EFAULT; | ||
| 538 | 549 | ||
| 539 | nr_pages = rds_pages_in_vec(&vec); | 550 | nr_pages = rds_pages_in_vec(vec); |
| 540 | if (nr_pages == 0) | 551 | if (nr_pages == 0) |
| 541 | return -EINVAL; | 552 | return -EINVAL; |
| 542 | 553 | ||
| @@ -558,15 +569,15 @@ int rds_rdma_extra_size(struct rds_rdma_args *args) | |||
| 558 | * Extract all arguments and set up the rdma_op | 569 | * Extract all arguments and set up the rdma_op |
| 559 | */ | 570 | */ |
| 560 | int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, | 571 | int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, |
| 561 | struct cmsghdr *cmsg) | 572 | struct cmsghdr *cmsg, |
| 573 | struct rds_iov_vector *vec) | ||
| 562 | { | 574 | { |
| 563 | struct rds_rdma_args *args; | 575 | struct rds_rdma_args *args; |
| 564 | struct rm_rdma_op *op = &rm->rdma; | 576 | struct rm_rdma_op *op = &rm->rdma; |
| 565 | int nr_pages; | 577 | int nr_pages; |
| 566 | unsigned int nr_bytes; | 578 | unsigned int nr_bytes; |
| 567 | struct page **pages = NULL; | 579 | struct page **pages = NULL; |
| 568 | struct rds_iovec iovstack[UIO_FASTIOV], *iovs = iovstack; | 580 | struct rds_iovec *iovs; |
| 569 | int iov_size; | ||
| 570 | unsigned int i, j; | 581 | unsigned int i, j; |
| 571 | int ret = 0; | 582 | int ret = 0; |
| 572 | 583 | ||
| @@ -586,31 +597,23 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, | |||
| 586 | goto out_ret; | 597 | goto out_ret; |
| 587 | } | 598 | } |
| 588 | 599 | ||
| 589 | /* Check whether to allocate the iovec area */ | 600 | if (vec->len != args->nr_local) { |
| 590 | iov_size = args->nr_local * sizeof(struct rds_iovec); | 601 | ret = -EINVAL; |
| 591 | if (args->nr_local > UIO_FASTIOV) { | 602 | goto out_ret; |
| 592 | iovs = sock_kmalloc(rds_rs_to_sk(rs), iov_size, GFP_KERNEL); | ||
| 593 | if (!iovs) { | ||
| 594 | ret = -ENOMEM; | ||
| 595 | goto out_ret; | ||
| 596 | } | ||
| 597 | } | 603 | } |
| 598 | 604 | ||
| 599 | if (copy_from_user(iovs, (struct rds_iovec __user *)(unsigned long) args->local_vec_addr, iov_size)) { | 605 | iovs = vec->iov; |
| 600 | ret = -EFAULT; | ||
| 601 | goto out; | ||
| 602 | } | ||
| 603 | 606 | ||
| 604 | nr_pages = rds_rdma_pages(iovs, args->nr_local); | 607 | nr_pages = rds_rdma_pages(iovs, args->nr_local); |
| 605 | if (nr_pages < 0) { | 608 | if (nr_pages < 0) { |
| 606 | ret = -EINVAL; | 609 | ret = -EINVAL; |
| 607 | goto out; | 610 | goto out_ret; |
| 608 | } | 611 | } |
| 609 | 612 | ||
| 610 | pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); | 613 | pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); |
| 611 | if (!pages) { | 614 | if (!pages) { |
| 612 | ret = -ENOMEM; | 615 | ret = -ENOMEM; |
| 613 | goto out; | 616 | goto out_ret; |
| 614 | } | 617 | } |
| 615 | 618 | ||
| 616 | op->op_write = !!(args->flags & RDS_RDMA_READWRITE); | 619 | op->op_write = !!(args->flags & RDS_RDMA_READWRITE); |
| @@ -623,7 +626,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, | |||
| 623 | op->op_sg = rds_message_alloc_sgs(rm, nr_pages); | 626 | op->op_sg = rds_message_alloc_sgs(rm, nr_pages); |
| 624 | if (!op->op_sg) { | 627 | if (!op->op_sg) { |
| 625 | ret = -ENOMEM; | 628 | ret = -ENOMEM; |
| 626 | goto out; | 629 | goto out_pages; |
| 627 | } | 630 | } |
| 628 | 631 | ||
| 629 | if (op->op_notify || op->op_recverr) { | 632 | if (op->op_notify || op->op_recverr) { |
| @@ -635,7 +638,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, | |||
| 635 | op->op_notifier = kmalloc(sizeof(struct rds_notifier), GFP_KERNEL); | 638 | op->op_notifier = kmalloc(sizeof(struct rds_notifier), GFP_KERNEL); |
| 636 | if (!op->op_notifier) { | 639 | if (!op->op_notifier) { |
| 637 | ret = -ENOMEM; | 640 | ret = -ENOMEM; |
| 638 | goto out; | 641 | goto out_pages; |
| 639 | } | 642 | } |
| 640 | op->op_notifier->n_user_token = args->user_token; | 643 | op->op_notifier->n_user_token = args->user_token; |
| 641 | op->op_notifier->n_status = RDS_RDMA_SUCCESS; | 644 | op->op_notifier->n_status = RDS_RDMA_SUCCESS; |
| @@ -681,7 +684,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, | |||
| 681 | */ | 684 | */ |
| 682 | ret = rds_pin_pages(iov->addr, nr, pages, !op->op_write); | 685 | ret = rds_pin_pages(iov->addr, nr, pages, !op->op_write); |
| 683 | if (ret < 0) | 686 | if (ret < 0) |
| 684 | goto out; | 687 | goto out_pages; |
| 685 | else | 688 | else |
| 686 | ret = 0; | 689 | ret = 0; |
| 687 | 690 | ||
| @@ -714,13 +717,11 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, | |||
| 714 | nr_bytes, | 717 | nr_bytes, |
| 715 | (unsigned int) args->remote_vec.bytes); | 718 | (unsigned int) args->remote_vec.bytes); |
| 716 | ret = -EINVAL; | 719 | ret = -EINVAL; |
| 717 | goto out; | 720 | goto out_pages; |
| 718 | } | 721 | } |
| 719 | op->op_bytes = nr_bytes; | 722 | op->op_bytes = nr_bytes; |
| 720 | 723 | ||
| 721 | out: | 724 | out_pages: |
| 722 | if (iovs != iovstack) | ||
| 723 | sock_kfree_s(rds_rs_to_sk(rs), iovs, iov_size); | ||
| 724 | kfree(pages); | 725 | kfree(pages); |
| 725 | out_ret: | 726 | out_ret: |
| 726 | if (ret) | 727 | if (ret) |
diff --git a/net/rds/rds.h b/net/rds/rds.h index 6bfaf05b63b2..4d2523100093 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h | |||
| @@ -386,6 +386,18 @@ static inline void rds_message_zcopy_queue_init(struct rds_msg_zcopy_queue *q) | |||
| 386 | INIT_LIST_HEAD(&q->zcookie_head); | 386 | INIT_LIST_HEAD(&q->zcookie_head); |
| 387 | } | 387 | } |
| 388 | 388 | ||
| 389 | struct rds_iov_vector { | ||
| 390 | struct rds_iovec *iov; | ||
| 391 | int len; | ||
| 392 | }; | ||
| 393 | |||
| 394 | struct rds_iov_vector_arr { | ||
| 395 | struct rds_iov_vector *vec; | ||
| 396 | int len; | ||
| 397 | int indx; | ||
| 398 | int incr; | ||
| 399 | }; | ||
| 400 | |||
| 389 | struct rds_message { | 401 | struct rds_message { |
| 390 | refcount_t m_refcount; | 402 | refcount_t m_refcount; |
| 391 | struct list_head m_sock_item; | 403 | struct list_head m_sock_item; |
| @@ -904,13 +916,13 @@ int rds_get_mr(struct rds_sock *rs, char __user *optval, int optlen); | |||
| 904 | int rds_get_mr_for_dest(struct rds_sock *rs, char __user *optval, int optlen); | 916 | int rds_get_mr_for_dest(struct rds_sock *rs, char __user *optval, int optlen); |
| 905 | int rds_free_mr(struct rds_sock *rs, char __user *optval, int optlen); | 917 | int rds_free_mr(struct rds_sock *rs, char __user *optval, int optlen); |
| 906 | void rds_rdma_drop_keys(struct rds_sock *rs); | 918 | void rds_rdma_drop_keys(struct rds_sock *rs); |
| 907 | int rds_rdma_extra_size(struct rds_rdma_args *args); | 919 | int rds_rdma_extra_size(struct rds_rdma_args *args, |
| 908 | int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, | 920 | struct rds_iov_vector *iov); |
| 909 | struct cmsghdr *cmsg); | ||
| 910 | int rds_cmsg_rdma_dest(struct rds_sock *rs, struct rds_message *rm, | ||
