Skip to content

Commit d15f9d6

Browse files
committed
libceph: check data_len in ->alloc_msg()
Only ->alloc_msg() should check data_len of the incoming message against the preallocated ceph_msg, doing it in the messenger is not right. The contract is that either ->alloc_msg() returns a ceph_msg which will fit all of the portions of the incoming message, or it returns NULL and possibly sets skip, signaling whether NULL is due to an -ENOMEM. ->alloc_msg() should be the only place where we make the skip/no-skip decision. I stumbled upon this while looking at con/osd ref counting. Right now, if we get a non-extent message with a larger data portion than we are prepared for, ->alloc_msg() returns a ceph_msg, and then, when we skip it in the messenger, we don't put the con/osd ref acquired in ceph_con_in_msg_alloc() (which is normally put in process_message()), so this also fixes a memory leak. An existing BUG_ON in ceph_msg_data_cursor_init() ensures we don't corrupt random memory should a buggy ->alloc_msg() return an unfit ceph_msg. While at it, I changed the "unknown tid" dout() to a pr_warn() to make sure all skips are seen and unified format strings. Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Reviewed-by: Alex Elder <elder@linaro.org>
1 parent 8b9558a commit d15f9d6

File tree

2 files changed

+18
-40
lines changed

2 files changed

+18
-40
lines changed

net/ceph/messenger.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2337,13 +2337,6 @@ static int read_partial_message(struct ceph_connection *con)
23372337
return ret;
23382338

23392339
BUG_ON(!con->in_msg ^ skip);
2340-
if (con->in_msg && data_len > con->in_msg->data_length) {
2341-
pr_warn("%s skipping long message (%u > %zd)\n",
2342-
__func__, data_len, con->in_msg->data_length);
2343-
ceph_msg_put(con->in_msg);
2344-
con->in_msg = NULL;
2345-
skip = 1;
2346-
}
23472340
if (skip) {
23482341
/* skip this message */
23492342
dout("alloc_msg said skip message\n");

net/ceph/osd_client.c

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2817,8 +2817,9 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
28172817
}
28182818

28192819
/*
2820-
* lookup and return message for incoming reply. set up reply message
2821-
* pages.
2820+
* Lookup and return message for incoming reply. Don't try to do
2821+
* anything about a larger than preallocated data portion of the
2822+
* message at the moment - for now, just skip the message.
28222823
*/
28232824
static struct ceph_msg *get_reply(struct ceph_connection *con,
28242825
struct ceph_msg_header *hdr,
@@ -2836,10 +2837,10 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
28362837
mutex_lock(&osdc->request_mutex);
28372838
req = __lookup_request(osdc, tid);
28382839
if (!req) {
2839-
*skip = 1;
2840+
pr_warn("%s osd%d tid %llu unknown, skipping\n",
2841+
__func__, osd->o_osd, tid);
28402842
m = NULL;
2841-
dout("get_reply unknown tid %llu from osd%d\n", tid,
2842-
osd->o_osd);
2843+
*skip = 1;
28432844
goto out;
28442845
}
28452846

@@ -2849,48 +2850,32 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
28492850
ceph_msg_revoke_incoming(req->r_reply);
28502851

28512852
if (front_len > req->r_reply->front_alloc_len) {
2852-
pr_warn("get_reply front %d > preallocated %d (%u#%llu)\n",
2853-
front_len, req->r_reply->front_alloc_len,
2854-
(unsigned int)con->peer_name.type,
2855-
le64_to_cpu(con->peer_name.num));
2853+
pr_warn("%s osd%d tid %llu front %d > preallocated %d\n",
2854+
__func__, osd->o_osd, req->r_tid, front_len,
2855+
req->r_reply->front_alloc_len);
28562856
m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front_len, GFP_NOFS,
28572857
false);
28582858
if (!m)
28592859
goto out;
28602860
ceph_msg_put(req->r_reply);
28612861
req->r_reply = m;
28622862
}
2863-
m = ceph_msg_get(req->r_reply);
2864-
2865-
if (data_len > 0) {
2866-
struct ceph_osd_data *osd_data;
28672863

2868-
/*
2869-
* XXX This is assuming there is only one op containing
2870-
* XXX page data. Probably OK for reads, but this
2871-
* XXX ought to be done more generally.
2872-
*/
2873-
osd_data = osd_req_op_extent_osd_data(req, 0);
2874-
if (osd_data->type == CEPH_OSD_DATA_TYPE_PAGES) {
2875-
if (osd_data->pages &&
2876-
unlikely(osd_data->length < data_len)) {
2877-
2878-
pr_warn("tid %lld reply has %d bytes we had only %llu bytes ready\n",
2879-
tid, data_len, osd_data->length);
2880-
*skip = 1;
2881-
ceph_msg_put(m);
2882-
m = NULL;
2883-
goto out;
2884-
}
2885-
}
2864+
if (data_len > req->r_reply->data_length) {
2865+
pr_warn("%s osd%d tid %llu data %d > preallocated %zu, skipping\n",
2866+
__func__, osd->o_osd, req->r_tid, data_len,
2867+
req->r_reply->data_length);
2868+
m = NULL;
2869+
*skip = 1;
2870+
goto out;
28862871
}
2887-
*skip = 0;
2872+
2873+
m = ceph_msg_get(req->r_reply);
28882874
dout("get_reply tid %lld %p\n", tid, m);
28892875

28902876
out:
28912877
mutex_unlock(&osdc->request_mutex);
28922878
return m;
2893-
28942879
}
28952880

28962881
static struct ceph_msg *alloc_msg(struct ceph_connection *con,

0 commit comments

Comments
 (0)