Skip to content

Commit

Permalink
libceph: fix corruption when using page_count 0 page in rbd
Browse files Browse the repository at this point in the history
commit 178eda29ca721842f2146378e73d43e0044c4166 upstream.

It has been reported that using ZFSonLinux on rbd will result in memory
corruption. The bug report can be found here:

openzfs/spl#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 <[email protected]>
Cc: Yehuda Sadeh <[email protected]>
Signed-off-by: Chunwei Chen <[email protected]>
Reviewed-by: Ilya Dryomov <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
tuxoko authored and engine95 committed Sep 25, 2017
1 parent cf70a91 commit 49b2c03
Showing 1 changed file with 19 additions and 1 deletion.
20 changes: 19 additions & 1 deletion net/ceph/messenger.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ static int ceph_tcp_sendmsg(struct socket *sock, struct kvec *iov,
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);
Expand All @@ -569,6 +569,24 @@ static int ceph_tcp_sendpage(struct socket *sock, struct page *page,
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.
Expand Down

0 comments on commit 49b2c03

Please sign in to comment.