Skip to contentSkip to conversation

iscsi-target: deadlock because of iscsit_get_tpg()

BUG Alexey Khoroshilov 2 participants 2 messages May 19, 2015

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

Hello,

Our tool reports a potential double lock because of quite strange code in iscsit_get_tpg().

drivers/target/iscsi/iscsi_target_tpg.c:
int iscsit_get_tpg(
struct iscsi_portal_group *tpg)

{
	int ret;

	ret = mutex_lock_interruptible(&tpg->tpg_access_lock);
	return ((ret != 0) || signal_pending(current)) ? -1 : 0;
}

If mutex_lock_interruptible() successfully acquires the mutex, but there is a pending signal, the function returns error, but it leaves the mutex held. Callers do not expect such behaviour that can lead to a deadlock.

Why the check for pending signal is needed here?

Found by Linux Driver Verification project (linuxtesting.org).

Similar dangerous pattern presents in a couple of other places:

drivers/target/iscsi/iscsi_target.c:
int iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg) {
...

	ret = down_interruptible(&tpg->np_login_sem);
	if ((ret != 0) || signal_pending(current))
		return -1;

drivers/target/target_core_sbc.c:
static sense_reason_t
sbc_compare_and_write(struct se_cmd *cmd)
{
...

	rc = down_interruptible(&dev->caw_sem);
	if ((rc != 0) || signal_pending(current)) {
		cmd->transport_complete_callback = NULL;
		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
	}

--
Alexey Khoroshilov
Linux Verification Center, ISPRAS

#1 lore raw

Hi Alexey,

Quoted 15 lines · Alexey Khoroshilov

Hello,

Our tool reports a potential double lock because of quite strange code in iscsit_get_tpg().

drivers/target/iscsi/iscsi_target_tpg.c:
int iscsit_get_tpg(
struct iscsi_portal_group *tpg)

{
	int ret;

	ret = mutex_lock_interruptible(&tpg->tpg_access_lock);
	return ((ret != 0) || signal_pending(current)) ? -1 : 0;
}
Quoted 6 lines

If mutex_lock_interruptible() successfully acquires the mutex, but there is a pending signal, the function returns error, but it leaves the mutex held. Callers do not expect such behaviour that can lead to a deadlock.

Why the check for pending signal is needed here?

Once upon a time, this was an un-interruptible lock. The signal pending was a check to allow userspace to break out of the operation with SIGINT.

AFAICT, there's no reason why this is necessary anymore.

Quoted 19 lines

Found by Linux Driver Verification project (linuxtesting.org).

Similar dangerous pattern presents in a couple of other places:

drivers/target/iscsi/iscsi_target.c:
int iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg) {
...

	ret = down_interruptible(&tpg->np_login_sem);
	if ((ret != 0) || signal_pending(current))
		return -1;

drivers/target/target_core_sbc.c:
static sense_reason_t
sbc_compare_and_write(struct se_cmd *cmd)
{
...

Quoted 7 lines
	rc = down_interruptible(&dev->caw_sem);
	if ((rc != 0) || signal_pending(current)) {
		cmd->transport_complete_callback = NULL;
		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
	}

Ditto on these.

Applying the following patch to target-pending/master.

Thank you,

--nab

drivers/target/iscsi/iscsi_target.c +1 -1
index 34871a6..74e6114 100644
@@ -230,7 +230,7 @@ int iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
         * Here we serialize access across the TIQN+TPG Tuple.
         */
        ret = down_interruptible(&tpg->np_login_sem);
-       if ((ret != 0) || signal_pending(current))
+       if (ret != 0)
                return -1;
 
        spin_lock_bh(&tpg->tpg_state_lock);
drivers/target/iscsi/iscsi_target_tpg.c +1 -4
index e8a2408..5e3295f 100644
@@ -161,10 +161,7 @@ struct iscsi_portal_group *iscsit_get_tpg_from_np(
 int iscsit_get_tpg(
        struct iscsi_portal_group *tpg)
 {
-       int ret;
-
-       ret = mutex_lock_interruptible(&tpg->tpg_access_lock);
-       return ((ret != 0) || signal_pending(current)) ? -1 : 0;
+       return mutex_lock_interruptible(&tpg->tpg_access_lock);
 }
 
 void iscsit_put_tpg(struct iscsi_portal_group *tpg)
drivers/target/target_core_sbc.c +1 -1
index 8855781..733824e 100644
@@ -568,7 +568,7 @@ sbc_compare_and_write(struct se_cmd *cmd)
         * comparision using SGLs at cmd->t_bidi_data_sg..
         */
        rc = down_interruptible(&dev->caw_sem);
-       if ((rc != 0) || signal_pending(current)) {
+       if (rc != 0) {
                cmd->transport_complete_callback = NULL;
                return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
        }