Skip to content

Commit ad66950

Browse files
bvanasschemartinkpetersen
authored andcommitted
scsi: target/core: Make sure that target_wait_for_sess_cmds() waits long enough
A session must only be released after all code that accesses the session structure has finished. Make sure that this is the case by introducing a new command counter per session that is only decremented after the .release_cmd() callback has finished. This patch fixes the following crash: BUG: KASAN: use-after-free in do_raw_spin_lock+0x1c/0x130 Read of size 4 at addr ffff8801534b16e4 by task rmdir/14805 CPU: 16 PID: 14805 Comm: rmdir Not tainted 4.18.0-rc2-dbg+ #5 Call Trace: dump_stack+0xa4/0xf5 print_address_description+0x6f/0x270 kasan_report+0x241/0x360 __asan_load4+0x78/0x80 do_raw_spin_lock+0x1c/0x130 _raw_spin_lock_irqsave+0x52/0x60 srpt_set_ch_state+0x27/0x70 [ib_srpt] srpt_disconnect_ch+0x1b/0xc0 [ib_srpt] srpt_close_session+0xa8/0x260 [ib_srpt] target_shutdown_sessions+0x170/0x180 [target_core_mod] core_tpg_del_initiator_node_acl+0xf3/0x200 [target_core_mod] target_fabric_nacl_base_release+0x25/0x30 [target_core_mod] config_item_release+0x9c/0x110 [configfs] config_item_put+0x26/0x30 [configfs] configfs_rmdir+0x3b8/0x510 [configfs] vfs_rmdir+0xb3/0x1e0 do_rmdir+0x262/0x2c0 do_syscall_64+0x77/0x230 entry_SYSCALL_64_after_hwframe+0x49/0xbe Cc: Nicholas Bellinger <nab@linux-iscsi.org> Cc: Mike Christie <mchristi@redhat.com> Cc: Christoph Hellwig <hch@lst.de> Cc: David Disseldorp <ddiss@suse.de> Cc: Hannes Reinecke <hare@suse.de> Signed-off-by: Bart Van Assche <bvanassche@acm.org> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent a95be38 commit ad66950

File tree

4 files changed

+32
-12
lines changed

4 files changed

+32
-12
lines changed

drivers/target/target_core_transport.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -224,19 +224,28 @@ void transport_subsystem_check_init(void)
224224
sub_api_initialized = 1;
225225
}
226226

227+
static void target_release_sess_cmd_refcnt(struct percpu_ref *ref)
228+
{
229+
struct se_session *sess = container_of(ref, typeof(*sess), cmd_count);
230+
231+
wake_up(&sess->cmd_list_wq);
232+
}
233+
227234
/**
228235
* transport_init_session - initialize a session object
229236
* @se_sess: Session object pointer.
230237
*
231238
* The caller must have zero-initialized @se_sess before calling this function.
232239
*/
233-
void transport_init_session(struct se_session *se_sess)
240+
int transport_init_session(struct se_session *se_sess)
234241
{
235242
INIT_LIST_HEAD(&se_sess->sess_list);
236243
INIT_LIST_HEAD(&se_sess->sess_acl_list);
237244
INIT_LIST_HEAD(&se_sess->sess_cmd_list);
238245
spin_lock_init(&se_sess->sess_cmd_lock);
239246
init_waitqueue_head(&se_sess->cmd_list_wq);
247+
return percpu_ref_init(&se_sess->cmd_count,
248+
target_release_sess_cmd_refcnt, 0, GFP_KERNEL);
240249
}
241250
EXPORT_SYMBOL(transport_init_session);
242251

@@ -247,14 +256,19 @@ EXPORT_SYMBOL(transport_init_session);
247256
struct se_session *transport_alloc_session(enum target_prot_op sup_prot_ops)
248257
{
249258
struct se_session *se_sess;
259+
int ret;
250260

251261
se_sess = kmem_cache_zalloc(se_sess_cache, GFP_KERNEL);
252262
if (!se_sess) {
253263
pr_err("Unable to allocate struct se_session from"
254264
" se_sess_cache\n");
255265
return ERR_PTR(-ENOMEM);
256266
}
257-
transport_init_session(se_sess);
267+
ret = transport_init_session(se_sess);
268+
if (ret < 0) {
269+
kfree(se_sess);
270+
return ERR_PTR(ret);
271+
}
258272
se_sess->sup_prot_ops = sup_prot_ops;
259273

260274
return se_sess;
@@ -578,6 +592,7 @@ void transport_free_session(struct se_session *se_sess)
578592
sbitmap_queue_free(&se_sess->sess_tag_pool);
579593
kvfree(se_sess->sess_cmd_map);
580594
}
595+
percpu_ref_exit(&se_sess->cmd_count);
581596
kmem_cache_free(se_sess_cache, se_sess);
582597
}
583598
EXPORT_SYMBOL(transport_free_session);
@@ -2716,6 +2731,7 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)
27162731
}
27172732
se_cmd->transport_state |= CMD_T_PRE_EXECUTE;
27182733
list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
2734+
percpu_ref_get(&se_sess->cmd_count);
27192735
out:
27202736
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
27212737

@@ -2746,15 +2762,15 @@ static void target_release_cmd_kref(struct kref *kref)
27462762
if (se_sess) {
27472763
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
27482764
list_del_init(&se_cmd->se_cmd_list);
2749-
if (se_sess->sess_tearing_down && list_empty(&se_sess->sess_cmd_list))
2750-
wake_up(&se_sess->cmd_list_wq);
27512765
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
27522766
}
27532767

27542768
target_free_cmd_mem(se_cmd);
27552769
se_cmd->se_tfo->release_cmd(se_cmd);
27562770
if (compl)
27572771
complete(compl);
2772+
2773+
percpu_ref_put(&se_sess->cmd_count);
27582774
}
27592775

27602776
/**
@@ -2883,6 +2899,8 @@ void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
28832899
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
28842900
se_sess->sess_tearing_down = 1;
28852901
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
2902+
2903+
percpu_ref_kill(&se_sess->cmd_count);
28862904
}
28872905
EXPORT_SYMBOL(target_sess_cmd_list_set_waiting);
28882906

@@ -2897,17 +2915,14 @@ void target_wait_for_sess_cmds(struct se_session *se_sess)
28972915

28982916
WARN_ON_ONCE(!se_sess->sess_tearing_down);
28992917

2900-
spin_lock_irq(&se_sess->sess_cmd_lock);
29012918
do {
2902-
ret = wait_event_lock_irq_timeout(
2903-
se_sess->cmd_list_wq,
2904-
list_empty(&se_sess->sess_cmd_list),
2905-
se_sess->sess_cmd_lock, 180 * HZ);
2919+
ret = wait_event_timeout(se_sess->cmd_list_wq,
2920+
percpu_ref_is_zero(&se_sess->cmd_count),
2921+
180 * HZ);
29062922
list_for_each_entry(cmd, &se_sess->sess_cmd_list, se_cmd_list)
29072923
target_show_cmd("session shutdown: still waiting for ",
29082924
cmd);
29092925
} while (ret <= 0);
2910-
spin_unlock_irq(&se_sess->sess_cmd_lock);
29112926
}
29122927
EXPORT_SYMBOL(target_wait_for_sess_cmds);
29132928

drivers/target/target_core_xcopy.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ static const struct target_core_fabric_ops xcopy_pt_tfo = {
474474

475475
int target_xcopy_setup_pt(void)
476476
{
477+
int ret;
478+
477479
xcopy_wq = alloc_workqueue("xcopy_wq", WQ_MEM_RECLAIM, 0);
478480
if (!xcopy_wq) {
479481
pr_err("Unable to allocate xcopy_wq\n");
@@ -491,7 +493,9 @@ int target_xcopy_setup_pt(void)
491493
INIT_LIST_HEAD(&xcopy_pt_nacl.acl_list);
492494
INIT_LIST_HEAD(&xcopy_pt_nacl.acl_sess_list);
493495
memset(&xcopy_pt_sess, 0, sizeof(struct se_session));
494-
transport_init_session(&xcopy_pt_sess);
496+
ret = transport_init_session(&xcopy_pt_sess);
497+
if (ret < 0)
498+
return ret;
495499

496500
xcopy_pt_nacl.se_tpg = &xcopy_pt_tpg;
497501
xcopy_pt_nacl.nacl_sess = &xcopy_pt_sess;

include/target/target_core_base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ struct se_session {
603603
struct se_node_acl *se_node_acl;
604604
struct se_portal_group *se_tpg;
605605
void *fabric_sess_ptr;
606+
struct percpu_ref cmd_count;
606607
struct list_head sess_list;
607608
struct list_head sess_acl_list;
608609
struct list_head sess_cmd_list;

include/target/target_core_fabric.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ struct se_session *target_setup_session(struct se_portal_group *,
126126
struct se_session *, void *));
127127
void target_remove_session(struct se_session *);
128128

129-
void transport_init_session(struct se_session *);
129+
int transport_init_session(struct se_session *se_sess);
130130
struct se_session *transport_alloc_session(enum target_prot_op);
131131
int transport_alloc_session_tags(struct se_session *, unsigned int,
132132
unsigned int);

0 commit comments

Comments
 (0)