From: Greg Kroah-Hartman Date: Mon, 26 Jun 2017 07:25:09 +0000 (+0200) Subject: 3.18-stable patches X-Git-Tag: v3.18.59~25 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=39bf807bfadda30f0ddff5581473a9bec63d5b9a;p=thirdparty%2Fkernel%2Fstable-queue.git 3.18-stable patches added patches: target-fix-kref-refcount-underflow-in-transport_cmd_finish_abort.patch --- diff --git a/queue-3.18/series b/queue-3.18/series index 71e689a3bb0..aebae9786f2 100644 --- a/queue-3.18/series +++ b/queue-3.18/series @@ -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 index 00000000000..1956c7fdbce --- /dev/null +++ b/queue-3.18/target-fix-kref-refcount-underflow-in-transport_cmd_finish_abort.patch @@ -0,0 +1,141 @@ +From 73d4e580ccc5c3e05cea002f18111f66c9c07034 Mon Sep 17 00:00:00 2001 +From: Nicholas Bellinger +Date: Fri, 2 Jun 2017 20:00:17 -0700 +Subject: target: Fix kref->refcount underflow in transport_cmd_finish_abort + +From: Nicholas Bellinger + +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 +Tested-by: Bart Van Assche +Cc: Mike Christie +Cc: Hannes Reinecke +Cc: Christoph Hellwig +Cc: Himanshu Madhani +Cc: Sagi Grimberg +Tested-by: Gary Guo +Tested-by: Chu Yuan Lin +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + 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)