aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2017-05-25 00:47:09 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2017-05-31 18:12:31 -0400
commit25cdda95fda78d22d44157da15aa7ea34be3c804 (patch)
treefd8a89f00e89c70cb8bb4798ee3857de22221694
parentf3cdbe39b2ab0636dec0d5d43b54f1061ce7566c (diff)
iscsi-target: Fix initial login PDU asynchronous socket close OOPs
This patch fixes a OOPs originally introduced by: commit bb048357dad6d604520c91586334c9c230366a14 Author: Nicholas Bellinger <nab@linux-iscsi.org> Date: Thu Sep 5 14:54:04 2013 -0700 iscsi-target: Add sk->sk_state_change to cleanup after TCP failure which would trigger a NULL pointer dereference when a TCP connection was closed asynchronously via iscsi_target_sk_state_change(), but only when the initial PDU processing in iscsi_target_do_login() from iscsi_np process context was blocked waiting for backend I/O to complete. To address this issue, this patch makes the following changes. First, it introduces some common helper functions used for checking socket closing state, checking login_flags, and atomically checking socket closing state + setting login_flags. Second, it introduces a LOGIN_FLAGS_INITIAL_PDU bit to know when a TCP connection has dropped via iscsi_target_sk_state_change(), but the initial PDU processing within iscsi_target_do_login() in iscsi_np context is still running. For this case, it sets LOGIN_FLAGS_CLOSED, but doesn't invoke schedule_delayed_work(). The original NULL pointer dereference case reported by MNC is now handled by iscsi_target_do_login() doing a iscsi_target_sk_check_close() before transitioning to FFP to determine when the socket has already closed, or iscsi_target_start_negotiation() if the login needs to exchange more PDUs (eg: iscsi_target_do_login returned 0) but the socket has closed. For both of these cases, the cleanup up of remaining connection resources will occur in iscsi_target_start_negotiation() from iscsi_np process context once the failure is detected. Finally, to handle to case where iscsi_target_sk_state_change() is called after the initial PDU procesing is complete, it now invokes conn->login_work -> iscsi_target_do_login_rx() to perform cleanup once existing iscsi_target_sk_check_close() checks detect connection failure. For this case, the cleanup of remaining connection resources will occur in iscsi_target_do_login_rx() from delayed workqueue process context once the failure is detected. Reported-by: Mike Christie <mchristi@redhat.com> Reviewed-by: Mike Christie <mchristi@redhat.com> Tested-by: Mike Christie <mchristi@redhat.com> Cc: Mike Christie <mchristi@redhat.com> Reported-by: Hannes Reinecke <hare@suse.com> Cc: Hannes Reinecke <hare@suse.com> Cc: Sagi Grimberg <sagi@grimberg.me> Cc: Varun Prakash <varun@chelsio.com> Cc: <stable@vger.kernel.org> # v3.12+ Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c194
-rw-r--r--include/target/iscsi/iscsi_target_core.h1
2 files changed, 133 insertions, 62 deletions
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 7ccc9c1cbfd1..6f88b31242b0 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -493,14 +493,60 @@ static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
493 493
494static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *); 494static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
495 495
496static bool iscsi_target_sk_state_check(struct sock *sk) 496static bool __iscsi_target_sk_check_close(struct sock *sk)
497{ 497{
498 if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) { 498 if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
499 pr_debug("iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE," 499 pr_debug("__iscsi_target_sk_check_close: TCP_CLOSE_WAIT|TCP_CLOSE,"
500 "returning FALSE\n"); 500 "returning FALSE\n");
501 return false; 501 return true;
502 } 502 }
503 return true; 503 return false;
504}
505
506static bool iscsi_target_sk_check_close(struct iscsi_conn *conn)
507{
508 bool state = false;
509
510 if (conn->sock) {
511 struct sock *sk = conn->sock->sk;
512
513 read_lock_bh(&sk->sk_callback_lock);
514 state = (__iscsi_target_sk_check_close(sk) ||
515 test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
516 read_unlock_bh(&sk->sk_callback_lock);
517 }
518 return state;
519}
520
521static bool iscsi_target_sk_check_flag(struct iscsi_conn *conn, unsigned int flag)
522{
523 bool state = false;
524
525 if (conn->sock) {
526 struct sock *sk = conn->sock->sk;
527
528 read_lock_bh(&sk->sk_callback_lock);
529 state = test_bit(flag, &conn->login_flags);
530 read_unlock_bh(&sk->sk_callback_lock);
531 }
532 return state;
533}
534
535static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned int flag)
536{
537 bool state = false;
538
539 if (conn->sock) {
540 struct sock *sk = conn->sock->sk;
541
542 write_lock_bh(&sk->sk_callback_lock);
543 state = (__iscsi_target_sk_check_close(sk) ||
544 test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
545 if (!state)
546 clear_bit(flag, &conn->login_flags);
547 write_unlock_bh(&sk->sk_callback_lock);
548 }
549 return state;
504} 550}
505 551
506static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login) 552static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
@@ -540,6 +586,20 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
540 586
541 pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n", 587 pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
542 conn, current->comm, current->pid); 588 conn, current->comm, current->pid);
589 /*
590 * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
591 * before initial PDU processing in iscsi_target_start_negotiation()
592 * has completed, go ahead and retry until it's cleared.
593 *
594 * Otherwise if the TCP connection drops while this is occuring,
595 * iscsi_target_start_negotiation() will detect the failure, call
596 * cancel_delayed_work_sync(&conn->login_work), and cleanup the
597 * remaining iscsi connection resources from iscsi_np process context.
598 */
599 if (iscsi_target_sk_check_flag(conn, LOGIN_FLAGS_INITIAL_PDU)) {
600 schedule_delayed_work(&conn->login_work, msecs_to_jiffies(10));
601 return;
602 }
543 603
544 spin_lock(&tpg->tpg_state_lock); 604 spin_lock(&tpg->tpg_state_lock);
545 state = (tpg->tpg_state == TPG_STATE_ACTIVE); 605 state = (tpg->tpg_state == TPG_STATE_ACTIVE);
@@ -547,26 +607,12 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
547 607
548 if (!state) { 608 if (!state) {
549 pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n"); 609 pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
550 iscsi_target_restore_sock_callbacks(conn); 610 goto err;
551 iscsi_target_login_drop(conn, login);
552 iscsit_deaccess_np(np, tpg, tpg_np);
553 return;
554 } 611 }
555 612
556 if (conn->sock) { 613 if (iscsi_target_sk_check_close(conn)) {
557 struct sock *sk = conn->sock->sk; 614 pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
558 615 goto err;
559 read_lock_bh(&sk->sk_callback_lock);
560 state = iscsi_target_sk_state_check(sk);
561 read_unlock_bh(&sk->sk_callback_lock);
562
563 if (!state) {
564 pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
565 iscsi_target_restore_sock_callbacks(conn);
566 iscsi_target_login_drop(conn, login);
567 iscsit_deaccess_np(np, tpg, tpg_np);
568 return;
569 }
570 } 616 }
571 617
572 conn->login_kworker = current; 618 conn->login_kworker = current;
@@ -584,34 +630,29 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
584 flush_signals(current); 630 flush_signals(current);
585 conn->login_kworker = NULL; 631 conn->login_kworker = NULL;
586 632
587 if (rc < 0) { 633 if (rc < 0)
588 iscsi_target_restore_sock_callbacks(conn); 634 goto err;
589 iscsi_target_login_drop(conn, login);
590 iscsit_deaccess_np(np, tpg, tpg_np);
591 return;
592 }
593 635
594 pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n", 636 pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
595 conn, current->comm, current->pid); 637 conn, current->comm, current->pid);
596 638
597 rc = iscsi_target_do_login(conn, login); 639 rc = iscsi_target_do_login(conn, login);
598 if (rc < 0) { 640 if (rc < 0) {
599 iscsi_target_restore_sock_callbacks(conn); 641 goto err;
600 iscsi_target_login_drop(conn, login);
601 iscsit_deaccess_np(np, tpg, tpg_np);
602 } else if (!rc) { 642 } else if (!rc) {
603 if (conn->sock) { 643 if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_READ_ACTIVE))
604 struct sock *sk = conn->sock->sk; 644 goto err;
605
606 write_lock_bh(&sk->sk_callback_lock);
607 clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
608 write_unlock_bh(&sk->sk_callback_lock);
609 }
610 } else if (rc == 1) { 645 } else if (rc == 1) {
611 iscsi_target_nego_release(conn); 646 iscsi_target_nego_release(conn);
612 iscsi_post_login_handler(np, conn, zero_tsih); 647 iscsi_post_login_handler(np, conn, zero_tsih);
613 iscsit_deaccess_np(np, tpg, tpg_np); 648 iscsit_deaccess_np(np, tpg, tpg_np);
614 } 649 }
650 return;
651
652err:
653 iscsi_target_restore_sock_callbacks(conn);
654 iscsi_target_login_drop(conn, login);
655 iscsit_deaccess_np(np, tpg, tpg_np);
615} 656}
616 657
617static void iscsi_target_do_cleanup(struct work_struct *work) 658static void iscsi_target_do_cleanup(struct work_struct *work)
@@ -659,31 +700,54 @@ static void iscsi_target_sk_state_change(struct sock *sk)
659 orig_state_change(sk); 700 orig_state_change(sk);
660 return; 701 return;
661 } 702 }
703 state = __iscsi_target_sk_check_close(sk);
704 pr_debug("__iscsi_target_sk_close_change: state: %d\n", state);
705
662 if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) { 706 if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
663 pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change" 707 pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
664 " conn: %p\n", conn); 708 " conn: %p\n", conn);
709 if (state)
710 set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
665 write_unlock_bh(&sk->sk_callback_lock); 711 write_unlock_bh(&sk->sk_callback_lock);
666 orig_state_change(sk); 712 orig_state_change(sk);
667 return; 713 return;
668 } 714 }
669 if (test_and_set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) { 715 if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
670 pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n", 716 pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
671 conn); 717 conn);
672 write_unlock_bh(&sk->sk_callback_lock); 718 write_unlock_bh(&sk->sk_callback_lock);
673 orig_state_change(sk); 719 orig_state_change(sk);
674 return; 720 return;
675 } 721 }
722 /*
723 * If the TCP connection has dropped, go ahead and set LOGIN_FLAGS_CLOSED,
724 * but only queue conn->login_work -> iscsi_target_do_login_rx()
725 * processing if LOGIN_FLAGS_INITIAL_PDU has already been cleared.
726 *
727 * When iscsi_target_do_login_rx() runs, iscsi_target_sk_check_close()
728 * will detect the dropped TCP connection from delayed workqueue context.
729 *
730 * If LOGIN_FLAGS_INITIAL_PDU is still set, which means the initial
731 * iscsi_target_start_negotiation() is running, iscsi_target_do_login()
732 * via iscsi_target_sk_check_close() or iscsi_target_start_negotiation()
733 * via iscsi_target_sk_check_and_clear() is responsible for detecting the
734 * dropped TCP connection in iscsi_np process context, and cleaning up
735 * the remaining iscsi connection resources.
736 */
737 if (state) {
738 pr_debug("iscsi_target_sk_state_change got failed state\n");
739 set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
740 state = test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
741 write_unlock_bh(&sk->sk_callback_lock);
676 742
677 state = iscsi_target_sk_state_check(sk); 743 orig_state_change(sk);
678 write_unlock_bh(&sk->sk_callback_lock);
679
680 pr_debug("iscsi_target_sk_state_change: state: %d\n", state);
681 744
682 if (!state) { 745 if (!state)
683 pr_debug("iscsi_target_sk_state_change got failed state\n"); 746 schedule_delayed_work(&conn->login_work, 0);
684 schedule_delayed_work(&conn->login_cleanup_work, 0);
685 return; 747 return;
686 } 748 }
749 write_unlock_bh(&sk->sk_callback_lock);
750
687 orig_state_change(sk); 751 orig_state_change(sk);
688} 752}
689 753
@@ -946,6 +1010,15 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
946 if (iscsi_target_handle_csg_one(conn, login) < 0) 1010 if (iscsi_target_handle_csg_one(conn, login) < 0)
947 return -1; 1011 return -1;
948 if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { 1012 if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
1013 /*
1014 * Check to make sure the TCP connection has not
1015 * dropped asynchronously while session reinstatement
1016 * was occuring in this kthread context, before
1017 * transitioning to full feature phase operation.
1018 */
1019 if (iscsi_target_sk_check_close(conn))
1020 return -1;
1021
949 login->tsih = conn->sess->tsih; 1022 login->tsih = conn->sess->tsih;
950 login->login_complete = 1; 1023 login->login_complete = 1;
951 iscsi_target_restore_sock_callbacks(conn); 1024 iscsi_target_restore_sock_callbacks(conn);
@@ -972,21 +1045,6 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
972 break; 1045 break;
973 } 1046 }
974 1047
975 if (conn->sock) {
976 struct sock *sk = conn->sock->sk;
977 bool state;
978
979 read_lock_bh(&sk->sk_callback_lock);
980 state = iscsi_target_sk_state_check(sk);
981 read_unlock_bh(&sk->sk_callback_lock);
982
983 if (!state) {
984 pr_debug("iscsi_target_do_login() failed state for"
985 " conn: %p\n", conn);
986 return -1;
987 }
988 }
989
990 return 0; 1048 return 0;
991} 1049}
992 1050
@@ -1255,10 +1313,22 @@ int iscsi_target_start_negotiation(
1255 1313
1256 write_lock_bh(&sk->sk_callback_lock); 1314 write_lock_bh(&sk->sk_callback_lock);
1257 set_bit(LOGIN_FLAGS_READY, &conn->login_flags); 1315 set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
1316 set_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
1258 write_unlock_bh(&sk->sk_callback_lock); 1317 write_unlock_bh(&sk->sk_callback_lock);
1259 } 1318 }
1260 1319 /*
1320 * If iscsi_target_do_login returns zero to signal more PDU
1321 * exchanges are required to complete the login, go ahead and
1322 * clear LOGIN_FLAGS_INITIAL_PDU but only if the TCP connection
1323 * is still active.
1324 *
1325 * Otherwise if TCP connection dropped asynchronously, go ahead
1326 * and perform connection cleanup now.
1327 */
1261 ret = iscsi_target_do_login(conn, login); 1328 ret = iscsi_target_do_login(conn, login);
1329 if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
1330 ret = -1;
1331
1262 if (ret < 0) { 1332 if (ret < 0) {
1263 cancel_delayed_work_sync(&conn->login_work); 1333 cancel_delayed_work_sync(&conn->login_work);
1264 cancel_delayed_work_sync(&conn->login_cleanup_work); 1334 cancel_delayed_work_sync(&conn->login_cleanup_work);
diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h
index 275581d483dd..5f17fb770477 100644
--- a/include/target/iscsi/iscsi_target_core.h
+++ b/include/target/iscsi/iscsi_target_core.h
@@ -557,6 +557,7 @@ struct iscsi_conn {
557#define LOGIN_FLAGS_READ_ACTIVE 1 557#define LOGIN_FLAGS_READ_ACTIVE 1
558#define LOGIN_FLAGS_CLOSED 2 558#define LOGIN_FLAGS_CLOSED 2
559#define LOGIN_FLAGS_READY 4 559#define LOGIN_FLAGS_READY 4
560#define LOGIN_FLAGS_INITIAL_PDU 8
560 unsigned long login_flags; 561 unsigned long login_flags;
561 struct delayed_work login_work; 562 struct delayed_work login_work;
562 struct delayed_work login_cleanup_work; 563 struct delayed_work login_cleanup_work;