aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIlya Dryomov <ilya.dryomov@inktank.com>2014-01-09 13:08:21 -0500
committerIlya Dryomov <ilya.dryomov@inktank.com>2014-01-14 04:27:47 -0500
commitf2be82b0058e90b5d9ac2cb896b4914276fb50ef (patch)
tree0754bf0d97cb300fc611a2ce4527649f29cc41c6
parent3f0a4ac55fe036902e3666be740da63528ad8639 (diff)
libceph: fix preallocation check in get_reply()
The check that makes sure that we have enough memory allocated to read in the entire header of the message in question is currently busted. It compares front_len of the incoming message with iov_len field of ceph_msg::front structure, which is used primarily to indicate the amount of data already read in, and not the size of the allocated buffer. Under certain conditions (e.g. a short read from a socket followed by that socket's shutdown and owning ceph_connection reset) this results in a warning similar to [85688.975866] libceph: get_reply front 198 > preallocated 122 (4#0) and, through another bug, leads to forever hung tasks and forced reboots. Fix this by comparing front_len with front_alloc_len field of struct ceph_msg, which stores the actual size of the buffer. Fixes: http://tracker.ceph.com/issues/5425 Signed-off-by: Ilya Dryomov <ilya.dryomov@inktank.com> Reviewed-by: Sage Weil <sage@inktank.com>
-rw-r--r--net/ceph/messenger.c3
-rw-r--r--net/ceph/osd_client.c4
2 files changed, 3 insertions, 4 deletions
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index f4d411c017e7..252ad4e01cf8 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -3130,7 +3130,6 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags,
3130 INIT_LIST_HEAD(&m->data); 3130 INIT_LIST_HEAD(&m->data);
3131 3131
3132 /* front */ 3132 /* front */
3133 m->front_alloc_len = front_len;
3134 if (front_len) { 3133 if (front_len) {
3135 if (front_len > PAGE_CACHE_SIZE) { 3134 if (front_len > PAGE_CACHE_SIZE) {
3136 m->front.iov_base = __vmalloc(front_len, flags, 3135 m->front.iov_base = __vmalloc(front_len, flags,
@@ -3147,7 +3146,7 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags,
3147 } else { 3146 } else {
3148 m->front.iov_base = NULL; 3147 m->front.iov_base = NULL;
3149 } 3148 }
3150 m->front.iov_len = front_len; 3149 m->front_alloc_len = m->front.iov_len = front_len;
3151 3150
3152 dout("ceph_msg_new %p front %d\n", m, front_len); 3151 dout("ceph_msg_new %p front %d\n", m, front_len);
3153 return m; 3152 return m;
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 7619c37bfed4..733195170490 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -2522,9 +2522,9 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
2522 req->r_reply, req->r_reply->con); 2522 req->r_reply, req->r_reply->con);
2523 ceph_msg_revoke_incoming(req->r_reply); 2523 ceph_msg_revoke_incoming(req->r_reply);
2524 2524
2525 if (front_len > req->r_reply->front.iov_len) { 2525 if (front_len > req->r_reply->front_alloc_len) {
2526 pr_warning("get_reply front %d > preallocated %d (%u#%llu)\n", 2526 pr_warning("get_reply front %d > preallocated %d (%u#%llu)\n",
2527 front_len, (int)req->r_reply->front.iov_len, 2527 front_len, req->r_reply->front_alloc_len,
2528 (unsigned int)con->peer_name.type, 2528 (unsigned int)con->peer_name.type,
2529 le64_to_cpu(con->peer_name.num)); 2529 le64_to_cpu(con->peer_name.num));
2530 m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front_len, GFP_NOFS, 2530 m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front_len, GFP_NOFS,