aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/target
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2016-01-08 01:09:27 -0500
committerNicholas Bellinger <nab@linux-iscsi.org>2016-01-20 04:34:15 -0500
commit21aaa23b0ebbd19334fa461370c03cbb076b3295 (patch)
treeee480a095123426c81a9aac8fed0fba8fe27fc74 /drivers/target
parentd36ad77f702356afb1009d2987b0ab55da4c7d57 (diff)
target: Obtain se_node_acl->acl_kref during get_initiator_node_acl
This patch addresses a long standing race where obtaining se_node_acl->acl_kref in __transport_register_session() happens a bit too late, and leaves open the potential for core_tpg_del_initiator_node_acl() to hit a NULL pointer dereference. Instead, take ->acl_kref in core_tpg_get_initiator_node_acl() while se_portal_group->acl_node_mutex is held, and move the final target_put_nacl() from transport_deregister_session() into transport_free_session() so that fabric driver login failure handling using the modern method to still work as expected. Also, update core_tpg_get_initiator_node_acl() to take an extra reference for dynamically generated acls for demo-mode, before returning to fabric caller. Also update iscsi-target sendtargets special case handling to use target_tpg_has_node_acl() when checking if demo_mode_discovery == true during discovery lookup. Note the existing wait_for_completion(&acl->acl_free_comp) in core_tpg_del_initiator_node_acl() does not change. Cc: Sagi Grimberg <sagig@mellanox.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Hannes Reinecke <hare@suse.de> Cc: Andy Grover <agrover@redhat.com> Cc: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/iscsi/iscsi_target.c2
-rw-r--r--drivers/target/target_core_tpg.c42
-rw-r--r--drivers/target/target_core_transport.c19
3 files changed, 55 insertions, 8 deletions
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index a81c0e5ca293..762b2d6ea1cc 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -3435,7 +3435,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
3435 3435
3436 if ((tpg->tpg_attrib.generate_node_acls == 0) && 3436 if ((tpg->tpg_attrib.generate_node_acls == 0) &&
3437 (tpg->tpg_attrib.demo_mode_discovery == 0) && 3437 (tpg->tpg_attrib.demo_mode_discovery == 0) &&
3438 (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg, 3438 (!target_tpg_has_node_acl(&tpg->tpg_se_tpg,
3439 cmd->conn->sess->sess_ops->InitiatorName))) { 3439 cmd->conn->sess->sess_ops->InitiatorName))) {
3440 continue; 3440 continue;
3441 } 3441 }
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 67be44da29ff..3608b1b5ecf7 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -75,9 +75,21 @@ struct se_node_acl *core_tpg_get_initiator_node_acl(
75 unsigned char *initiatorname) 75 unsigned char *initiatorname)
76{ 76{
77 struct se_node_acl *acl; 77 struct se_node_acl *acl;
78 78 /*
79 * Obtain se_node_acl->acl_kref using fabric driver provided
80 * initiatorname[] during node acl endpoint lookup driven by
81 * new se_session login.
82 *
83 * The reference is held until se_session shutdown -> release
84 * occurs via fabric driver invoked transport_deregister_session()
85 * or transport_free_session() code.
86 */
79 mutex_lock(&tpg->acl_node_mutex); 87 mutex_lock(&tpg->acl_node_mutex);
80 acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); 88 acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
89 if (acl) {
90 if (!kref_get_unless_zero(&acl->acl_kref))
91 acl = NULL;
92 }
81 mutex_unlock(&tpg->acl_node_mutex); 93 mutex_unlock(&tpg->acl_node_mutex);
82 94
83 return acl; 95 return acl;
@@ -224,6 +236,25 @@ static void target_add_node_acl(struct se_node_acl *acl)
224 acl->initiatorname); 236 acl->initiatorname);
225} 237}
226 238
239bool target_tpg_has_node_acl(struct se_portal_group *tpg,
240 const char *initiatorname)
241{
242 struct se_node_acl *acl;
243 bool found = false;
244
245 mutex_lock(&tpg->acl_node_mutex);
246 list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
247 if (!strcmp(acl->initiatorname, initiatorname)) {
248 found = true;
249 break;
250 }
251 }
252 mutex_unlock(&tpg->acl_node_mutex);
253
254 return found;
255}
256EXPORT_SYMBOL(target_tpg_has_node_acl);
257
227struct se_node_acl *core_tpg_check_initiator_node_acl( 258struct se_node_acl *core_tpg_check_initiator_node_acl(
228 struct se_portal_group *tpg, 259 struct se_portal_group *tpg,
229 unsigned char *initiatorname) 260 unsigned char *initiatorname)
@@ -240,6 +271,15 @@ struct se_node_acl *core_tpg_check_initiator_node_acl(
240 acl = target_alloc_node_acl(tpg, initiatorname); 271 acl = target_alloc_node_acl(tpg, initiatorname);
241 if (!acl) 272 if (!acl)
242 return NULL; 273 return NULL;
274 /*
275 * When allocating a dynamically generated node_acl, go ahead
276 * and take the extra kref now before returning to the fabric
277 * driver caller.
278 *
279 * Note this reference will be released at session shutdown
280 * time within transport_free_session() code.
281 */
282 kref_get(&acl->acl_kref);
243 acl->dynamic_node_acl = 1; 283 acl->dynamic_node_acl = 1;
244 284
245 /* 285 /*
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 7b05ebf8053c..a7c1bb54cf72 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -341,7 +341,6 @@ void __transport_register_session(
341 &buf[0], PR_REG_ISID_LEN); 341 &buf[0], PR_REG_ISID_LEN);
342 se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]); 342 se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]);
343 } 343 }
344 kref_get(&se_nacl->acl_kref);
345 344
346 spin_lock_irq(&se_nacl->nacl_sess_lock); 345 spin_lock_irq(&se_nacl->nacl_sess_lock);
347 /* 346 /*
@@ -432,6 +431,7 @@ void target_put_nacl(struct se_node_acl *nacl)
432{ 431{
433 kref_put(&nacl->acl_kref, target_complete_nacl); 432 kref_put(&nacl->acl_kref, target_complete_nacl);
434} 433}
434EXPORT_SYMBOL(target_put_nacl);
435 435
436void transport_deregister_session_configfs(struct se_session *se_sess) 436void transport_deregister_session_configfs(struct se_session *se_sess)
437{ 437{
@@ -464,6 +464,15 @@ EXPORT_SYMBOL(transport_deregister_session_configfs);
464 464
465void transport_free_session(struct se_session *se_sess) 465void transport_free_session(struct se_session *se_sess)
466{ 466{
467 struct se_node_acl *se_nacl = se_sess->se_node_acl;
468 /*
469 * Drop the se_node_acl->nacl_kref obtained from within
470 * core_tpg_get_initiator_node_acl().
471 */
472 if (se_nacl) {
473 se_sess->se_node_acl = NULL;
474 target_put_nacl(se_nacl);
475 }
467 if (se_sess->sess_cmd_map) { 476 if (se_sess->sess_cmd_map) {
468 percpu_ida_destroy(&se_sess->sess_tag_pool); 477 percpu_ida_destroy(&se_sess->sess_tag_pool);
469 kvfree(se_sess->sess_cmd_map); 478 kvfree(se_sess->sess_cmd_map);
@@ -478,7 +487,7 @@ void transport_deregister_session(struct se_session *se_sess)
478 const struct target_core_fabric_ops *se_tfo; 487 const struct target_core_fabric_ops *se_tfo;
479 struct se_node_acl *se_nacl; 488 struct se_node_acl *se_nacl;
480 unsigned long flags; 489 unsigned long flags;
481 bool comp_nacl = true, drop_nacl = false; 490 bool drop_nacl = false;
482 491
483 if (!se_tpg) { 492 if (!se_tpg) {
484 transport_free_session(se_sess); 493 transport_free_session(se_sess);
@@ -510,18 +519,16 @@ void transport_deregister_session(struct se_session *se_sess)
510 if (drop_nacl) { 519 if (drop_nacl) {
511 core_tpg_wait_for_nacl_pr_ref(se_nacl); 520 core_tpg_wait_for_nacl_pr_ref(se_nacl);
512 core_free_device_list_for_node(se_nacl, se_tpg); 521 core_free_device_list_for_node(se_nacl, se_tpg);
522 se_sess->se_node_acl = NULL;
513 kfree(se_nacl); 523 kfree(se_nacl);
514 comp_nacl = false;
515 } 524 }
516 pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n", 525 pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
517 se_tpg->se_tpg_tfo->get_fabric_name()); 526 se_tpg->se_tpg_tfo->get_fabric_name());
518 /* 527 /*
519 * If last kref is dropping now for an explicit NodeACL, awake sleeping 528 * If last kref is dropping now for an explicit NodeACL, awake sleeping
520 * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group 529 * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
521 * removal context. 530 * removal context from within transport_free_session() code.
522 */ 531 */
523 if (se_nacl && comp_nacl)
524 target_put_nacl(se_nacl);
525 532
526 transport_free_session(se_sess); 533 transport_free_session(se_sess);
527} 534}