]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 26 Jun 2017 07:25:09 +0000 (09:25 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 26 Jun 2017 07:25:09 +0000 (09:25 +0200)
added patches:
target-fix-kref-refcount-underflow-in-transport_cmd_finish_abort.patch

queue-3.18/series
queue-3.18/target-fix-kref-refcount-underflow-in-transport_cmd_finish_abort.patch [new file with mode: 0644]

index 71e689a3bb0a76583df739bf5773b0c41d033ff5..aebae9786f20d012f118fec4957b90ffebcf6d47 100644 (file)
@@ -6,3 +6,4 @@ cifs-improve-readdir-verbosity.patch
 signal-only-reschedule-timers-on-signals-timers-have-sent.patch
 powerpc-kprobes-pause-function_graph-tracing-during-jprobes-handling.patch
 input-i8042-add-fujitsu-lifebook-ah544-to-notimeout-list.patch
+target-fix-kref-refcount-underflow-in-transport_cmd_finish_abort.patch
diff --git a/queue-3.18/target-fix-kref-refcount-underflow-in-transport_cmd_finish_abort.patch b/queue-3.18/target-fix-kref-refcount-underflow-in-transport_cmd_finish_abort.patch
new file mode 100644 (file)
index 0000000..1956c7f
--- /dev/null
@@ -0,0 +1,141 @@
+From 73d4e580ccc5c3e05cea002f18111f66c9c07034 Mon Sep 17 00:00:00 2001
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+Date: Fri, 2 Jun 2017 20:00:17 -0700
+Subject: target: Fix kref->refcount underflow in transport_cmd_finish_abort
+
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+
+commit 73d4e580ccc5c3e05cea002f18111f66c9c07034 upstream.
+
+This patch fixes a se_cmd->cmd_kref underflow during CMD_T_ABORTED
+when a fabric driver drops it's second reference from below the
+target_core_tmr.c based callers of transport_cmd_finish_abort().
+
+Recently with the conversion of kref to refcount_t, this bug was
+manifesting itself as:
+
+[705519.601034] refcount_t: underflow; use-after-free.
+[705519.604034] INFO: NMI handler (kgdb_nmi_handler) took too long to run: 20116.512 msecs
+[705539.719111] ------------[ cut here ]------------
+[705539.719117] WARNING: CPU: 3 PID: 26510 at lib/refcount.c:184 refcount_sub_and_test+0x33/0x51
+
+Since the original kref atomic_t based kref_put() didn't check for
+underflow and only invoked the final callback when zero was reached,
+this bug did not manifest in practice since all se_cmd memory is
+using preallocated tags.
+
+To address this, go ahead and propigate the existing return from
+transport_put_cmd() up via transport_cmd_finish_abort(), and
+change transport_cmd_finish_abort() + core_tmr_handle_tas_abort()
+callers to only do their local target_put_sess_cmd() if necessary.
+
+Reported-by: Bart Van Assche <bart.vanassche@sandisk.com>
+Tested-by: Bart Van Assche <bart.vanassche@sandisk.com>
+Cc: Mike Christie <mchristi@redhat.com>
+Cc: Hannes Reinecke <hare@suse.de>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Himanshu Madhani <himanshu.madhani@qlogic.com>
+Cc: Sagi Grimberg <sagig@mellanox.com>
+Tested-by: Gary Guo <ghg@datera.io>
+Tested-by: Chu Yuan Lin <cyl@datera.io>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/target/target_core_internal.h  |    2 +-
+ drivers/target/target_core_tmr.c       |   16 ++++++++--------
+ drivers/target/target_core_transport.c |    9 ++++++---
+ 3 files changed, 15 insertions(+), 12 deletions(-)
+
+--- a/drivers/target/target_core_internal.h
++++ b/drivers/target/target_core_internal.h
+@@ -92,7 +92,7 @@ int  init_se_kmem_caches(void);
+ void  release_se_kmem_caches(void);
+ u32   scsi_get_new_index(scsi_index_t);
+ void  transport_subsystem_check_init(void);
+-void  transport_cmd_finish_abort(struct se_cmd *, int);
++int   transport_cmd_finish_abort(struct se_cmd *, int);
+ unsigned char *transport_dump_cmd_direction(struct se_cmd *);
+ void  transport_dump_dev_state(struct se_device *, char *, int *);
+ void  transport_dump_dev_info(struct se_device *, struct se_lun *,
+--- a/drivers/target/target_core_tmr.c
++++ b/drivers/target/target_core_tmr.c
+@@ -78,7 +78,7 @@ void core_tmr_release_req(struct se_tmr_
+       kfree(tmr);
+ }
+-static void core_tmr_handle_tas_abort(struct se_cmd *cmd, int tas)
++static int core_tmr_handle_tas_abort(struct se_cmd *cmd, int tas)
+ {
+       unsigned long flags;
+       bool remove = true, send_tas;
+@@ -94,7 +94,7 @@ static void core_tmr_handle_tas_abort(st
+               transport_send_task_abort(cmd);
+       }
+-      transport_cmd_finish_abort(cmd, remove);
++      return transport_cmd_finish_abort(cmd, remove);
+ }
+ static int target_check_cdb_and_preempt(struct list_head *list,
+@@ -190,8 +190,8 @@ void core_tmr_abort_task(
+               cancel_work_sync(&se_cmd->work);
+               transport_wait_for_tasks(se_cmd);
+-              transport_cmd_finish_abort(se_cmd, true);
+-              target_put_sess_cmd(se_cmd);
++              if (!transport_cmd_finish_abort(se_cmd, true))
++                      target_put_sess_cmd(se_cmd);
+               printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for"
+                               " ref_tag: %d\n", ref_tag);
+@@ -291,8 +291,8 @@ static void core_tmr_drain_tmr_list(
+               cancel_work_sync(&cmd->work);
+               transport_wait_for_tasks(cmd);
+-              transport_cmd_finish_abort(cmd, 1);
+-              target_put_sess_cmd(cmd);
++              if (!transport_cmd_finish_abort(cmd, 1))
++                      target_put_sess_cmd(cmd);
+       }
+ }
+@@ -390,8 +390,8 @@ static void core_tmr_drain_state_list(
+               cancel_work_sync(&cmd->work);
+               transport_wait_for_tasks(cmd);
+-              core_tmr_handle_tas_abort(cmd, tas);
+-              target_put_sess_cmd(cmd);
++              if (!core_tmr_handle_tas_abort(cmd, tas))
++                      target_put_sess_cmd(cmd);
+       }
+ }
+--- a/drivers/target/target_core_transport.c
++++ b/drivers/target/target_core_transport.c
+@@ -604,9 +604,10 @@ static void transport_lun_remove_cmd(str
+               percpu_ref_put(&lun->lun_ref);
+ }
+-void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
++int transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
+ {
+       bool ack_kref = (cmd->se_cmd_flags & SCF_ACK_KREF);
++      int ret = 0;
+       if (cmd->se_cmd_flags & SCF_SE_LUN_CMD)
+               transport_lun_remove_cmd(cmd);
+@@ -618,9 +619,11 @@ void transport_cmd_finish_abort(struct s
+               cmd->se_tfo->aborted_task(cmd);
+       if (transport_cmd_check_stop_to_fabric(cmd))
+-              return;
++              return 1;
+       if (remove && ack_kref)
+-              transport_put_cmd(cmd);
++              ret = transport_put_cmd(cmd);
++
++      return ret;
+ }
+ static void target_complete_failure_work(struct work_struct *work)