| From 178eda29ca721842f2146378e73d43e0044c4166 Mon Sep 17 00:00:00 2001 |
| From: Chunwei Chen <tuxoko@gmail.com> |
| Date: Wed, 23 Apr 2014 12:35:09 +0800 |
| Subject: libceph: fix corruption when using page_count 0 page in rbd |
| |
| From: Chunwei Chen <tuxoko@gmail.com> |
| |
| commit 178eda29ca721842f2146378e73d43e0044c4166 upstream. |
| |
| It has been reported that using ZFSonLinux on rbd will result in memory |
| corruption. The bug report can be found here: |
| |
| https://github.com/zfsonlinux/spl/issues/241 |
| http://tracker.ceph.com/issues/7790 |
| |
| The reason is that ZFS will send pages with page_count 0 into rbd, which in |
| turns send them to tcp_sendpage. However, tcp_sendpage cannot deal with |
| page_count 0, as it will do get_page and put_page, and erroneously free the |
| page. |
| |
| This type of issue has been noted before, and handled in iscsi, drbd, |
| etc. So, rbd should also handle this. This fix address this issue by fall back |
| to slower sendmsg when page_count 0 detected. |
| |
| Cc: Sage Weil <sage@inktank.com> |
| Cc: Yehuda Sadeh <yehuda@inktank.com> |
| Signed-off-by: Chunwei Chen <tuxoko@gmail.com> |
| Reviewed-by: Ilya Dryomov <ilya.dryomov@inktank.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/ceph/messenger.c | 20 +++++++++++++++++++- |
| 1 file changed, 19 insertions(+), 1 deletion(-) |
| |
| --- a/net/ceph/messenger.c |
| +++ b/net/ceph/messenger.c |
| @@ -556,7 +556,7 @@ static int ceph_tcp_sendmsg(struct socke |
| return r; |
| } |
| |
| -static int ceph_tcp_sendpage(struct socket *sock, struct page *page, |
| +static int __ceph_tcp_sendpage(struct socket *sock, struct page *page, |
| int offset, size_t size, bool more) |
| { |
| int flags = MSG_DONTWAIT | MSG_NOSIGNAL | (more ? MSG_MORE : MSG_EOR); |
| @@ -569,6 +569,24 @@ static int ceph_tcp_sendpage(struct sock |
| return ret; |
| } |
| |
| +static int ceph_tcp_sendpage(struct socket *sock, struct page *page, |
| + int offset, size_t size, bool more) |
| +{ |
| + int ret; |
| + struct kvec iov; |
| + |
| + /* sendpage cannot properly handle pages with page_count == 0, |
| + * we need to fallback to sendmsg if that's the case */ |
| + if (page_count(page) >= 1) |
| + return __ceph_tcp_sendpage(sock, page, offset, size, more); |
| + |
| + iov.iov_base = kmap(page) + offset; |
| + iov.iov_len = size; |
| + ret = ceph_tcp_sendmsg(sock, &iov, 1, size, more); |
| + kunmap(page); |
| + |
| + return ret; |
| +} |
| |
| /* |
| * Shutdown/close the socket for the given connection. |