From b06eef6eab8e4a241f88385527ac4d1844abc18d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 13 Jun 2014 04:05:16 +0000 Subject: [PATCH 1/8] iscsi-target: Convert chap_server_compute_md5 to use kstrtoul This patch converts chap_server_compute_md5() from simple_strtoul() to kstrtoul usage(). This addresses the case where a empty 'CHAP_I=' key value received during mutual authentication would be converted to a '0' by simple_strtoul(), instead of failing the login attempt. Reported-by: Tejas Vaykole Tested-by: Tejas Vaykole Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_auth.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 19b842c3e0b3..9430eea7c0d6 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -174,7 +174,6 @@ static int chap_server_compute_md5( char *nr_out_ptr, unsigned int *nr_out_len) { - char *endptr; unsigned long id; unsigned char id_as_uchar; unsigned char digest[MD5_SIGNATURE_SIZE]; @@ -320,9 +319,14 @@ static int chap_server_compute_md5( } if (type == HEX) - id = simple_strtoul(&identifier[2], &endptr, 0); + ret = kstrtoul(&identifier[2], 0, &id); else - id = simple_strtoul(identifier, &endptr, 0); + ret = kstrtoul(identifier, 0, &id); + + if (ret < 0) { + pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret); + goto out; + } if (id > 255) { pr_err("chap identifier: %lu greater than 255\n", id); goto out; From e4fae2318b5ddd7aec0e65871f1b455b796cf33d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 13 Jun 2014 04:28:31 +0000 Subject: [PATCH 2/8] iscsi-target; Enforce 1024 byte maximum for CHAP_C key value This patch adds a check in chap_server_compute_md5() to enforce a 1024 byte maximum for the CHAP_C key value following the requirement in RFC-3720 Section 11.1.4: "..., C and R are large-binary-values and their binary length (not the length of the character string that represents them in encoded form) MUST not exceed 1024 bytes." Reported-by: rahul.rane Tested-by: rahul.rane Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_auth.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 9430eea7c0d6..ab4915c0d933 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -355,6 +355,10 @@ static int chap_server_compute_md5( pr_err("Unable to convert incoming challenge\n"); goto out; } + if (challenge_len > 1024) { + pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); + goto out; + } /* * During mutual authentication, the CHAP_C generated by the * initiator must not match the original CHAP_C generated by From 83ff42fcce070801a3aa1cd6a3269d7426271a8d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 16 Jun 2014 20:25:54 +0000 Subject: [PATCH 3/8] target: Fix left-over se_lun->lun_sep pointer OOPs This patch fixes a left-over se_lun->lun_sep pointer OOPs when one of the /sys/kernel/config/target/$FABRIC/$WWPN/$TPGT/lun/$LUN/alua* attributes is accessed after the $DEVICE symlink has been removed. To address this bug, go ahead and clear se_lun->lun_sep memory in core_dev_unexport(), so that the existing checks for show/store ALUA attributes in target_core_fabric_configfs.c work as expected. Reported-by: Sebastian Herbszt Tested-by: Sebastian Herbszt Cc: stable@vger.kernel.org Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 11d26fe65bfb..98da90167159 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -616,6 +616,7 @@ void core_dev_unexport( dev->export_count--; spin_unlock(&hba->device_lock); + lun->lun_sep = NULL; lun->lun_se_dev = NULL; } From 683497566d48f86e04d026de1ee658dd74fc1077 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 17 Jun 2014 21:54:38 +0000 Subject: [PATCH 4/8] iscsi-target: Explicily clear login response PDU in exception path This patch adds a explicit memset to the login response PDU exception path in iscsit_tx_login_rsp(). This addresses a regression bug introduced in commit baa4d64b where the initiator would end up not receiving the login response and associated status class + detail, before closing the login connection. Reported-by: Christophe Vu-Brugier Tested-by: Christophe Vu-Brugier Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 53e157cb8c54..fd90b28f1d94 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -1295,6 +1295,8 @@ int iscsit_tx_login_rsp(struct iscsi_conn *conn, u8 status_class, u8 status_deta login->login_failed = 1; iscsit_collect_login_stats(conn, status_class, status_detail); + memset(&login->rsp[0], 0, ISCSI_HDR_LEN); + hdr = (struct iscsi_login_rsp *)&login->rsp[0]; hdr->opcode = ISCSI_OP_LOGIN_RSP; hdr->status_class = status_class; From b43f1886e4d3ea3f68665eaea96526ccdd53741d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 17 Jun 2014 22:23:03 +0000 Subject: [PATCH 5/8] tcm_loop: Fix memory leak in tcm_loop_submission_work error path This patch fixes a tcm_loop_cmd descriptor memory leak in the tcm_loop_submission_work() error path, and would result in warnings about leaked tcm_loop_cmd_cache objects at module unload time. Go ahead and invoke kmem_cache_free() to release tl_cmd back to tcm_loop_cmd_cache before calling sc->scsi_done(). Reported-by: Sebastian Herbszt Tested-by: Sebastian Herbszt Signed-off-by: Nicholas Bellinger --- drivers/target/loopback/tcm_loop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 6d2f37578b29..8c64b8776a96 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -239,6 +239,7 @@ static void tcm_loop_submission_work(struct work_struct *work) return; out_done: + kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); sc->scsi_done(sc); return; } From 97c99b47ac58bacb7c09e1f47d5d184434f6b06a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 20 Jun 2014 10:59:57 -0700 Subject: [PATCH 6/8] iscsi-target: Avoid rejecting incorrect ITT for Data-Out This patch changes iscsit_check_dataout_hdr() to dump the incoming Data-Out payload when the received ITT is not associated with a WRITE, instead of calling iscsit_reject_cmd() for the non WRITE ITT descriptor. This addresses a bug where an initiator sending an Data-Out for an ITT associated with a READ would end up generating a reject for the READ, eventually resulting in list corruption. Reported-by: Santosh Kulkarni Reported-by: Arshad Hussain Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 5663f4d19d02..1f4c794f5fcc 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1309,7 +1309,7 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, if (cmd->data_direction != DMA_TO_DEVICE) { pr_err("Command ITT: 0x%08x received DataOUT for a" " NON-WRITE command.\n", cmd->init_task_tag); - return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf); + return iscsit_dump_data_payload(conn, payload_length, 1); } se_cmd = &cmd->se_cmd; iscsit_mod_dataout_timer(cmd); From ac5ccdba3a1659b3517e7e99ef7d35a6a2d77cf4 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 19 Jun 2014 21:22:56 +0300 Subject: [PATCH 7/8] iovec: move memcpy_from/toiovecend to lib/iovec.c ERROR: "memcpy_fromiovecend" [drivers/vhost/vhost_scsi.ko] undefined! commit 9f977ef7b671f6169eca78bf40f230fe84b7c7e5 vhost-scsi: Include prot_bytes into expected data transfer length in target-pending makes drivers/vhost/scsi.c call memcpy_fromiovecend(). This function is not available when CONFIG_NET is not enabled. socket.h already includes uio.h, so no callers need updating. Reported-by: Randy Dunlap Cc: Stephen Rothwell Cc: "David S. Miller" Signed-off-by: David S. Miller Signed-off-by: Michael S. Tsirkin Signed-off-by: Nicholas Bellinger --- include/linux/socket.h | 4 --- include/linux/uio.h | 5 +++- lib/iovec.c | 55 ++++++++++++++++++++++++++++++++++++++++++ net/core/iovec.c | 55 ------------------------------------------ 4 files changed, 59 insertions(+), 60 deletions(-) diff --git a/include/linux/socket.h b/include/linux/socket.h index 8e98297f1388..ec538fc287a6 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -305,8 +305,6 @@ struct ucred { /* IPX options */ #define IPX_TYPE 1 -extern int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, - int offset, int len); extern int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, @@ -315,8 +313,6 @@ extern unsigned long iov_pages(const struct iovec *iov, int offset, unsigned long nr_segs); extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode); -extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, - int offset, int len); extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr); extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); diff --git a/include/linux/uio.h b/include/linux/uio.h index e2231e47cec1..04c8c4bb4927 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -111,6 +111,9 @@ static inline void iov_iter_reexpand(struct iov_iter *i, size_t count) int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len); int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len); - +int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, + int offset, int len); +int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, + int offset, int len); #endif diff --git a/lib/iovec.c b/lib/iovec.c index 454baa88bf27..7a7c2da4cddf 100644 --- a/lib/iovec.c +++ b/lib/iovec.c @@ -51,3 +51,58 @@ int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len) return 0; } EXPORT_SYMBOL(memcpy_toiovec); + +/* + * Copy kernel to iovec. Returns -EFAULT on error. + */ + +int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, + int offset, int len) +{ + int copy; + for (; len > 0; ++iov) { + /* Skip over the finished iovecs */ + if (unlikely(offset >= iov->iov_len)) { + offset -= iov->iov_len; + continue; + } + copy = min_t(unsigned int, iov->iov_len - offset, len); + if (copy_to_user(iov->iov_base + offset, kdata, copy)) + return -EFAULT; + offset = 0; + kdata += copy; + len -= copy; + } + + return 0; +} +EXPORT_SYMBOL(memcpy_toiovecend); + +/* + * Copy iovec to kernel. Returns -EFAULT on error. + */ + +int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, + int offset, int len) +{ + /* Skip over the finished iovecs */ + while (offset >= iov->iov_len) { + offset -= iov->iov_len; + iov++; + } + + while (len > 0) { + u8 __user *base = iov->iov_base + offset; + int copy = min_t(unsigned int, len, iov->iov_len - offset); + + offset = 0; + if (copy_from_user(kdata, base, copy)) + return -EFAULT; + len -= copy; + kdata += copy; + iov++; + } + + return 0; +} +EXPORT_SYMBOL(memcpy_fromiovecend); diff --git a/net/core/iovec.c b/net/core/iovec.c index b61869429f4c..827dd6beb49c 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -74,61 +74,6 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a return err; } -/* - * Copy kernel to iovec. Returns -EFAULT on error. - */ - -int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, - int offset, int len) -{ - int copy; - for (; len > 0; ++iov) { - /* Skip over the finished iovecs */ - if (unlikely(offset >= iov->iov_len)) { - offset -= iov->iov_len; - continue; - } - copy = min_t(unsigned int, iov->iov_len - offset, len); - if (copy_to_user(iov->iov_base + offset, kdata, copy)) - return -EFAULT; - offset = 0; - kdata += copy; - len -= copy; - } - - return 0; -} -EXPORT_SYMBOL(memcpy_toiovecend); - -/* - * Copy iovec to kernel. Returns -EFAULT on error. - */ - -int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, - int offset, int len) -{ - /* Skip over the finished iovecs */ - while (offset >= iov->iov_len) { - offset -= iov->iov_len; - iov++; - } - - while (len > 0) { - u8 __user *base = iov->iov_base + offset; - int copy = min_t(unsigned int, len, iov->iov_len - offset); - - offset = 0; - if (copy_from_user(kdata, base, copy)) - return -EFAULT; - len -= copy; - kdata += copy; - iov++; - } - - return 0; -} -EXPORT_SYMBOL(memcpy_fromiovecend); - /* * And now for the all-in-one: copy and checksum from a user iovec * directly to a datagram From 81a9c5e72bdf7109a65102ca61d8cbd722cf4021 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Mon, 23 Jun 2014 13:42:37 -0400 Subject: [PATCH 8/8] iscsi-target: fix iscsit_del_np deadlock on unload On uniprocessor preemptible kernel, target core deadlocks on unload. The following events happen: * iscsit_del_np is called * it calls send_sig(SIGINT, np->np_thread, 1); * the scheduler switches to the np_thread * the np_thread is woken up, it sees that kthread_should_stop() returns false, so it doesn't terminate * the np_thread clears signals with flush_signals(current); and goes back to sleep in iscsit_accept_np * the scheduler switches back to iscsit_del_np * iscsit_del_np calls kthread_stop(np->np_thread); * the np_thread is waiting in iscsit_accept_np and it doesn't respond to kthread_stop The deadlock could be resolved if the administrator sends SIGINT signal to the np_thread with killall -INT iscsi_np The reproducible deadlock was introduced in commit db6077fd0b7dd41dc6ff18329cec979379071f87, but the thread-stopping code was racy even before. This patch fixes the problem. Using kthread_should_stop to stop the np_thread is unreliable, so we test np_thread_state instead. If np_thread_state equals ISCSI_NP_THREAD_SHUTDOWN, the thread exits. Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_login.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index fecb69535a15..5e71ac609418 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -1216,7 +1216,7 @@ old_sess_out: static int __iscsi_target_login_thread(struct iscsi_np *np) { u8 *buffer, zero_tsih = 0; - int ret = 0, rc, stop; + int ret = 0, rc; struct iscsi_conn *conn = NULL; struct iscsi_login *login; struct iscsi_portal_group *tpg = NULL; @@ -1230,6 +1230,9 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; complete(&np->np_restart_comp); + } else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) { + spin_unlock_bh(&np->np_thread_lock); + goto exit; } else { np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; } @@ -1422,10 +1425,8 @@ old_sess_out: } out: - stop = kthread_should_stop(); - /* Wait for another socket.. */ - if (!stop) - return 1; + return 1; + exit: iscsi_stop_login_thread_timer(np); spin_lock_bh(&np->np_thread_lock); @@ -1442,7 +1443,7 @@ int iscsi_target_login_thread(void *arg) allow_signal(SIGINT); - while (!kthread_should_stop()) { + while (1) { ret = __iscsi_target_login_thread(np); /* * We break and exit here unless another sock_accept() call