diff --git a/debian/changelog b/debian/changelog index c66b79a61..f5481b06c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -65,6 +65,11 @@ linux (3.16.2-3) UNRELEASED; urgency=medium practice this meant we didn't see them until they appear in unstable * udeb: Fix typo in dependencies of speakup-modules (fixes FTBFS on mips64el due to interaction with another bug in kernel-wedge) + * libceph: Apply critical fixes: + - set last_piece in ceph_msg_data_pages_cursor_init() correctly + - gracefully handle large reply messages from the mon + - add process_one_ticket() helper + - do not hard code max auth ticket len [ Ian Campbell ] * [armel/orion5x] udeb: Include mvmdio in nic-modules udeb. diff --git a/debian/patches/bugfix/all/libceph-add-process_one_ticket-helper.patch b/debian/patches/bugfix/all/libceph-add-process_one_ticket-helper.patch new file mode 100644 index 000000000..8d97981b1 --- /dev/null +++ b/debian/patches/bugfix/all/libceph-add-process_one_ticket-helper.patch @@ -0,0 +1,275 @@ +From: Ilya Dryomov +Date: Mon, 8 Sep 2014 17:25:34 +0400 +Subject: libceph: add process_one_ticket() helper +Origin: https://git.kernel.org/linus/597cda357716a3cf8d994cb11927af917c8d71fa + +Add a helper for processing individual cephx auth tickets. Needed for +the next commit, which deals with allocating ticket buffers. (Most of +the diff here is whitespace - view with git diff -b). + +Cc: stable@vger.kernel.org +Signed-off-by: Ilya Dryomov +Reviewed-by: Sage Weil +--- + net/ceph/auth_x.c | 228 +++++++++++++++++++++++++++++------------------------- + 1 file changed, 124 insertions(+), 104 deletions(-) + +diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c +index 96238ba..0eb146d 100644 +--- a/net/ceph/auth_x.c ++++ b/net/ceph/auth_x.c +@@ -129,17 +129,131 @@ static void remove_ticket_handler(struct ceph_auth_client *ac, + kfree(th); + } + ++static int process_one_ticket(struct ceph_auth_client *ac, ++ struct ceph_crypto_key *secret, ++ void **p, void *end, ++ void *dbuf, void *ticket_buf) ++{ ++ struct ceph_x_info *xi = ac->private; ++ int type; ++ u8 tkt_struct_v, blob_struct_v; ++ struct ceph_x_ticket_handler *th; ++ void *dp, *dend; ++ int dlen; ++ char is_enc; ++ struct timespec validity; ++ struct ceph_crypto_key old_key; ++ void *tp, *tpend; ++ struct ceph_timespec new_validity; ++ struct ceph_crypto_key new_session_key; ++ struct ceph_buffer *new_ticket_blob; ++ unsigned long new_expires, new_renew_after; ++ u64 new_secret_id; ++ int ret; ++ ++ ceph_decode_need(p, end, sizeof(u32) + 1, bad); ++ ++ type = ceph_decode_32(p); ++ dout(" ticket type %d %s\n", type, ceph_entity_type_name(type)); ++ ++ tkt_struct_v = ceph_decode_8(p); ++ if (tkt_struct_v != 1) ++ goto bad; ++ ++ th = get_ticket_handler(ac, type); ++ if (IS_ERR(th)) { ++ ret = PTR_ERR(th); ++ goto out; ++ } ++ ++ /* blob for me */ ++ dlen = ceph_x_decrypt(secret, p, end, dbuf, ++ TEMP_TICKET_BUF_LEN); ++ if (dlen <= 0) { ++ ret = dlen; ++ goto out; ++ } ++ dout(" decrypted %d bytes\n", dlen); ++ dp = dbuf; ++ dend = dp + dlen; ++ ++ tkt_struct_v = ceph_decode_8(&dp); ++ if (tkt_struct_v != 1) ++ goto bad; ++ ++ memcpy(&old_key, &th->session_key, sizeof(old_key)); ++ ret = ceph_crypto_key_decode(&new_session_key, &dp, dend); ++ if (ret) ++ goto out; ++ ++ ceph_decode_copy(&dp, &new_validity, sizeof(new_validity)); ++ ceph_decode_timespec(&validity, &new_validity); ++ new_expires = get_seconds() + validity.tv_sec; ++ new_renew_after = new_expires - (validity.tv_sec / 4); ++ dout(" expires=%lu renew_after=%lu\n", new_expires, ++ new_renew_after); ++ ++ /* ticket blob for service */ ++ ceph_decode_8_safe(p, end, is_enc, bad); ++ tp = ticket_buf; ++ if (is_enc) { ++ /* encrypted */ ++ dout(" encrypted ticket\n"); ++ dlen = ceph_x_decrypt(&old_key, p, end, ticket_buf, ++ TEMP_TICKET_BUF_LEN); ++ if (dlen < 0) { ++ ret = dlen; ++ goto out; ++ } ++ dlen = ceph_decode_32(&tp); ++ } else { ++ /* unencrypted */ ++ ceph_decode_32_safe(p, end, dlen, bad); ++ ceph_decode_need(p, end, dlen, bad); ++ ceph_decode_copy(p, ticket_buf, dlen); ++ } ++ tpend = tp + dlen; ++ dout(" ticket blob is %d bytes\n", dlen); ++ ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad); ++ blob_struct_v = ceph_decode_8(&tp); ++ new_secret_id = ceph_decode_64(&tp); ++ ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend); ++ if (ret) ++ goto out; ++ ++ /* all is well, update our ticket */ ++ ceph_crypto_key_destroy(&th->session_key); ++ if (th->ticket_blob) ++ ceph_buffer_put(th->ticket_blob); ++ th->session_key = new_session_key; ++ th->ticket_blob = new_ticket_blob; ++ th->validity = new_validity; ++ th->secret_id = new_secret_id; ++ th->expires = new_expires; ++ th->renew_after = new_renew_after; ++ dout(" got ticket service %d (%s) secret_id %lld len %d\n", ++ type, ceph_entity_type_name(type), th->secret_id, ++ (int)th->ticket_blob->vec.iov_len); ++ xi->have_keys |= th->service; ++ ++out: ++ return ret; ++ ++bad: ++ ret = -EINVAL; ++ goto out; ++} ++ + static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, + struct ceph_crypto_key *secret, + void *buf, void *end) + { +- struct ceph_x_info *xi = ac->private; +- int num; + void *p = buf; +- int ret; + char *dbuf; + char *ticket_buf; + u8 reply_struct_v; ++ u32 num; ++ int ret; + + dbuf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); + if (!dbuf) +@@ -150,112 +264,18 @@ static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, + if (!ticket_buf) + goto out_dbuf; + +- ceph_decode_need(&p, end, 1 + sizeof(u32), bad); +- reply_struct_v = ceph_decode_8(&p); ++ ceph_decode_8_safe(&p, end, reply_struct_v, bad); + if (reply_struct_v != 1) +- goto bad; +- num = ceph_decode_32(&p); +- dout("%d tickets\n", num); +- while (num--) { +- int type; +- u8 tkt_struct_v, blob_struct_v; +- struct ceph_x_ticket_handler *th; +- void *dp, *dend; +- int dlen; +- char is_enc; +- struct timespec validity; +- struct ceph_crypto_key old_key; +- void *tp, *tpend; +- struct ceph_timespec new_validity; +- struct ceph_crypto_key new_session_key; +- struct ceph_buffer *new_ticket_blob; +- unsigned long new_expires, new_renew_after; +- u64 new_secret_id; +- +- ceph_decode_need(&p, end, sizeof(u32) + 1, bad); +- +- type = ceph_decode_32(&p); +- dout(" ticket type %d %s\n", type, ceph_entity_type_name(type)); +- +- tkt_struct_v = ceph_decode_8(&p); +- if (tkt_struct_v != 1) +- goto bad; +- +- th = get_ticket_handler(ac, type); +- if (IS_ERR(th)) { +- ret = PTR_ERR(th); +- goto out; +- } +- +- /* blob for me */ +- dlen = ceph_x_decrypt(secret, &p, end, dbuf, +- TEMP_TICKET_BUF_LEN); +- if (dlen <= 0) { +- ret = dlen; +- goto out; +- } +- dout(" decrypted %d bytes\n", dlen); +- dend = dbuf + dlen; +- dp = dbuf; +- +- tkt_struct_v = ceph_decode_8(&dp); +- if (tkt_struct_v != 1) +- goto bad; ++ return -EINVAL; + +- memcpy(&old_key, &th->session_key, sizeof(old_key)); +- ret = ceph_crypto_key_decode(&new_session_key, &dp, dend); +- if (ret) +- goto out; ++ ceph_decode_32_safe(&p, end, num, bad); ++ dout("%d tickets\n", num); + +- ceph_decode_copy(&dp, &new_validity, sizeof(new_validity)); +- ceph_decode_timespec(&validity, &new_validity); +- new_expires = get_seconds() + validity.tv_sec; +- new_renew_after = new_expires - (validity.tv_sec / 4); +- dout(" expires=%lu renew_after=%lu\n", new_expires, +- new_renew_after); +- +- /* ticket blob for service */ +- ceph_decode_8_safe(&p, end, is_enc, bad); +- tp = ticket_buf; +- if (is_enc) { +- /* encrypted */ +- dout(" encrypted ticket\n"); +- dlen = ceph_x_decrypt(&old_key, &p, end, ticket_buf, +- TEMP_TICKET_BUF_LEN); +- if (dlen < 0) { +- ret = dlen; +- goto out; +- } +- dlen = ceph_decode_32(&tp); +- } else { +- /* unencrypted */ +- ceph_decode_32_safe(&p, end, dlen, bad); +- ceph_decode_need(&p, end, dlen, bad); +- ceph_decode_copy(&p, ticket_buf, dlen); +- } +- tpend = tp + dlen; +- dout(" ticket blob is %d bytes\n", dlen); +- ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad); +- blob_struct_v = ceph_decode_8(&tp); +- new_secret_id = ceph_decode_64(&tp); +- ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend); ++ while (num--) { ++ ret = process_one_ticket(ac, secret, &p, end, ++ dbuf, ticket_buf); + if (ret) + goto out; +- +- /* all is well, update our ticket */ +- ceph_crypto_key_destroy(&th->session_key); +- if (th->ticket_blob) +- ceph_buffer_put(th->ticket_blob); +- th->session_key = new_session_key; +- th->ticket_blob = new_ticket_blob; +- th->validity = new_validity; +- th->secret_id = new_secret_id; +- th->expires = new_expires; +- th->renew_after = new_renew_after; +- dout(" got ticket service %d (%s) secret_id %lld len %d\n", +- type, ceph_entity_type_name(type), th->secret_id, +- (int)th->ticket_blob->vec.iov_len); +- xi->have_keys |= th->service; + } + + ret = 0; diff --git a/debian/patches/bugfix/all/libceph-do-not-hard-code-max-auth-ticket-len.patch b/debian/patches/bugfix/all/libceph-do-not-hard-code-max-auth-ticket-len.patch new file mode 100644 index 000000000..2166f246b --- /dev/null +++ b/debian/patches/bugfix/all/libceph-do-not-hard-code-max-auth-ticket-len.patch @@ -0,0 +1,195 @@ +From: Ilya Dryomov +Date: Tue, 9 Sep 2014 19:39:15 +0400 +Subject: libceph: do not hard code max auth ticket len +Origin: https://git.kernel.org/linus/c27a3e4d667fdcad3db7b104f75659478e0c68d8 + +We hard code cephx auth ticket buffer size to 256 bytes. This isn't +enough for any moderate setups and, in case tickets themselves are not +encrypted, leads to buffer overflows (ceph_x_decrypt() errors out, but +ceph_decode_copy() doesn't - it's just a memcpy() wrapper). Since the +buffer is allocated dynamically anyway, allocated it a bit later, at +the point where we know how much is going to be needed. + +Fixes: http://tracker.ceph.com/issues/8979 + +Cc: stable@vger.kernel.org +Signed-off-by: Ilya Dryomov +Reviewed-by: Sage Weil +--- + net/ceph/auth_x.c | 64 +++++++++++++++++++++++++------------------------------ + 1 file changed, 29 insertions(+), 35 deletions(-) + +diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c +index 0eb146d..de6662b 100644 +--- a/net/ceph/auth_x.c ++++ b/net/ceph/auth_x.c +@@ -13,8 +13,6 @@ + #include "auth_x.h" + #include "auth_x_protocol.h" + +-#define TEMP_TICKET_BUF_LEN 256 +- + static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed); + + static int ceph_x_is_authenticated(struct ceph_auth_client *ac) +@@ -64,7 +62,7 @@ static int ceph_x_encrypt(struct ceph_crypto_key *secret, + } + + static int ceph_x_decrypt(struct ceph_crypto_key *secret, +- void **p, void *end, void *obuf, size_t olen) ++ void **p, void *end, void **obuf, size_t olen) + { + struct ceph_x_encrypt_header head; + size_t head_len = sizeof(head); +@@ -75,8 +73,14 @@ static int ceph_x_decrypt(struct ceph_crypto_key *secret, + return -EINVAL; + + dout("ceph_x_decrypt len %d\n", len); +- ret = ceph_decrypt2(secret, &head, &head_len, obuf, &olen, +- *p, len); ++ if (*obuf == NULL) { ++ *obuf = kmalloc(len, GFP_NOFS); ++ if (!*obuf) ++ return -ENOMEM; ++ olen = len; ++ } ++ ++ ret = ceph_decrypt2(secret, &head, &head_len, *obuf, &olen, *p, len); + if (ret) + return ret; + if (head.struct_v != 1 || le64_to_cpu(head.magic) != CEPHX_ENC_MAGIC) +@@ -131,18 +135,19 @@ static void remove_ticket_handler(struct ceph_auth_client *ac, + + static int process_one_ticket(struct ceph_auth_client *ac, + struct ceph_crypto_key *secret, +- void **p, void *end, +- void *dbuf, void *ticket_buf) ++ void **p, void *end) + { + struct ceph_x_info *xi = ac->private; + int type; + u8 tkt_struct_v, blob_struct_v; + struct ceph_x_ticket_handler *th; ++ void *dbuf = NULL; + void *dp, *dend; + int dlen; + char is_enc; + struct timespec validity; + struct ceph_crypto_key old_key; ++ void *ticket_buf = NULL; + void *tp, *tpend; + struct ceph_timespec new_validity; + struct ceph_crypto_key new_session_key; +@@ -167,8 +172,7 @@ static int process_one_ticket(struct ceph_auth_client *ac, + } + + /* blob for me */ +- dlen = ceph_x_decrypt(secret, p, end, dbuf, +- TEMP_TICKET_BUF_LEN); ++ dlen = ceph_x_decrypt(secret, p, end, &dbuf, 0); + if (dlen <= 0) { + ret = dlen; + goto out; +@@ -195,20 +199,25 @@ static int process_one_ticket(struct ceph_auth_client *ac, + + /* ticket blob for service */ + ceph_decode_8_safe(p, end, is_enc, bad); +- tp = ticket_buf; + if (is_enc) { + /* encrypted */ + dout(" encrypted ticket\n"); +- dlen = ceph_x_decrypt(&old_key, p, end, ticket_buf, +- TEMP_TICKET_BUF_LEN); ++ dlen = ceph_x_decrypt(&old_key, p, end, &ticket_buf, 0); + if (dlen < 0) { + ret = dlen; + goto out; + } ++ tp = ticket_buf; + dlen = ceph_decode_32(&tp); + } else { + /* unencrypted */ + ceph_decode_32_safe(p, end, dlen, bad); ++ ticket_buf = kmalloc(dlen, GFP_NOFS); ++ if (!ticket_buf) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ tp = ticket_buf; + ceph_decode_need(p, end, dlen, bad); + ceph_decode_copy(p, ticket_buf, dlen); + } +@@ -237,6 +246,8 @@ static int process_one_ticket(struct ceph_auth_client *ac, + xi->have_keys |= th->service; + + out: ++ kfree(ticket_buf); ++ kfree(dbuf); + return ret; + + bad: +@@ -249,21 +260,10 @@ static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, + void *buf, void *end) + { + void *p = buf; +- char *dbuf; +- char *ticket_buf; + u8 reply_struct_v; + u32 num; + int ret; + +- dbuf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); +- if (!dbuf) +- return -ENOMEM; +- +- ret = -ENOMEM; +- ticket_buf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); +- if (!ticket_buf) +- goto out_dbuf; +- + ceph_decode_8_safe(&p, end, reply_struct_v, bad); + if (reply_struct_v != 1) + return -EINVAL; +@@ -272,22 +272,15 @@ static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, + dout("%d tickets\n", num); + + while (num--) { +- ret = process_one_ticket(ac, secret, &p, end, +- dbuf, ticket_buf); ++ ret = process_one_ticket(ac, secret, &p, end); + if (ret) +- goto out; ++ return ret; + } + +- ret = 0; +-out: +- kfree(ticket_buf); +-out_dbuf: +- kfree(dbuf); +- return ret; ++ return 0; + + bad: +- ret = -EINVAL; +- goto out; ++ return -EINVAL; + } + + static int ceph_x_build_authorizer(struct ceph_auth_client *ac, +@@ -603,13 +596,14 @@ static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac, + struct ceph_x_ticket_handler *th; + int ret = 0; + struct ceph_x_authorize_reply reply; ++ void *preply = &reply; + void *p = au->reply_buf; + void *end = p + sizeof(au->reply_buf); + + th = get_ticket_handler(ac, au->service); + if (IS_ERR(th)) + return PTR_ERR(th); +- ret = ceph_x_decrypt(&th->session_key, &p, end, &reply, sizeof(reply)); ++ ret = ceph_x_decrypt(&th->session_key, &p, end, &preply, sizeof(reply)); + if (ret < 0) + return ret; + if (ret != sizeof(reply)) diff --git a/debian/patches/bugfix/all/libceph-gracefully-handle-large-reply-messages-from-.patch b/debian/patches/bugfix/all/libceph-gracefully-handle-large-reply-messages-from-.patch new file mode 100644 index 000000000..1244951d2 --- /dev/null +++ b/debian/patches/bugfix/all/libceph-gracefully-handle-large-reply-messages-from-.patch @@ -0,0 +1,36 @@ +From: Sage Weil +Date: Mon, 4 Aug 2014 07:01:54 -0700 +Subject: libceph: gracefully handle large reply messages from the mon +Origin: https://git.kernel.org/linus/73c3d4812b4c755efeca0140f606f83772a39ce4 + +We preallocate a few of the message types we get back from the mon. If we +get a larger message than we are expecting, fall back to trying to allocate +a new one instead of blindly using the one we have. + +CC: stable@vger.kernel.org +Signed-off-by: Sage Weil +Reviewed-by: Ilya Dryomov +--- + net/ceph/mon_client.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c +index 067d3af..61fcfc3 100644 +--- a/net/ceph/mon_client.c ++++ b/net/ceph/mon_client.c +@@ -1181,7 +1181,15 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, + if (!m) { + pr_info("alloc_msg unknown type %d\n", type); + *skip = 1; ++ } else if (front_len > m->front_alloc_len) { ++ pr_warning("mon_alloc_msg front %d > prealloc %d (%u#%llu)\n", ++ front_len, m->front_alloc_len, ++ (unsigned int)con->peer_name.type, ++ le64_to_cpu(con->peer_name.num)); ++ ceph_msg_put(m); ++ m = ceph_msg_new(type, front_len, GFP_NOFS, false); + } ++ + return m; + } + diff --git a/debian/patches/bugfix/all/libceph-set-last_piece-in-ceph_msg_data_pages_cursor.patch b/debian/patches/bugfix/all/libceph-set-last_piece-in-ceph_msg_data_pages_cursor.patch new file mode 100644 index 000000000..fda0fc14c --- /dev/null +++ b/debian/patches/bugfix/all/libceph-set-last_piece-in-ceph_msg_data_pages_cursor.patch @@ -0,0 +1,50 @@ +From: Ilya Dryomov +Date: Fri, 8 Aug 2014 12:43:39 +0400 +Subject: libceph: set last_piece in ceph_msg_data_pages_cursor_init() + correctly +Origin: https://git.kernel.org/linus/5f740d7e1531099b888410e6bab13f68da9b1a4d + +Determining ->last_piece based on the value of ->page_offset + length +is incorrect because length here is the length of the entire message. +->last_piece set to false even if page array data item length is <= +PAGE_SIZE, which results in invalid length passed to +ceph_tcp_{send,recv}page() and causes various asserts to fire. + + # cat pages-cursor-init.sh + #!/bin/bash + rbd create --size 10 --image-format 2 foo + FOO_DEV=$(rbd map foo) + dd if=/dev/urandom of=$FOO_DEV bs=1M &>/dev/null + rbd snap create foo@snap + rbd snap protect foo@snap + rbd clone foo@snap bar + # rbd_resize calls librbd rbd_resize(), size is in bytes + ./rbd_resize bar $(((4 << 20) + 512)) + rbd resize --size 10 bar + BAR_DEV=$(rbd map bar) + # trigger a 512-byte copyup -- 512-byte page array data item + dd if=/dev/urandom of=$BAR_DEV bs=1M count=1 seek=5 + +The problem exists only in ceph_msg_data_pages_cursor_init(), +ceph_msg_data_pages_advance() does the right thing. The size_t cast is +unnecessary. + +Cc: stable@vger.kernel.org # 3.10+ +Signed-off-by: Ilya Dryomov +Reviewed-by: Sage Weil +Reviewed-by: Alex Elder +--- + net/ceph/messenger.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/ceph/messenger.c ++++ b/net/ceph/messenger.c +@@ -900,7 +900,7 @@ static void ceph_msg_data_pages_cursor_i + BUG_ON(page_count > (int)USHRT_MAX); + cursor->page_count = (unsigned short)page_count; + BUG_ON(length > SIZE_MAX - cursor->page_offset); +- cursor->last_piece = (size_t)cursor->page_offset + length <= PAGE_SIZE; ++ cursor->last_piece = cursor->page_offset + cursor->resid <= PAGE_SIZE; + } + + static struct page * diff --git a/debian/patches/series b/debian/patches/series index 3f79464f0..550c9a662 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -115,6 +115,10 @@ bugfix/all/mnt-Add-tests-for-unprivileged-remount-cases-that-ha.patch debian/i2o-disable-i2o_ext_adaptec-on-64bit.patch bugfix/all/aic94xx-remove-broken-fallback-for-missing-ctrl-a.patch bugfix/all/builddeb-put-the-dbg-files-into-the-correct-director.patch +bugfix/all/libceph-set-last_piece-in-ceph_msg_data_pages_cursor.patch +bugfix/all/libceph-gracefully-handle-large-reply-messages-from-.patch +bugfix/all/libceph-add-process_one_ticket-helper.patch +bugfix/all/libceph-do-not-hard-code-max-auth-ticket-len.patch # Miscellaneous features features/all/efi-autoload-efivars.patch