diff options
| author | Ilya Dryomov <ilya.dryomov@inktank.com> | 2014-01-09 13:08:21 -0500 |
|---|---|---|
| committer | Ilya Dryomov <ilya.dryomov@inktank.com> | 2014-01-14 04:27:47 -0500 |
| commit | f2be82b0058e90b5d9ac2cb896b4914276fb50ef (patch) | |
| tree | 0754bf0d97cb300fc611a2ce4527649f29cc41c6 /net | |
| parent | 3f0a4ac55fe036902e3666be740da63528ad8639 (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>
Diffstat (limited to 'net')
| -rw-r--r-- | net/ceph/messenger.c | 3 | ||||
| -rw-r--r-- | net/ceph/osd_client.c | 4 |
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, |
