Commit 20e01bba authored by Takashi Sakamoto's avatar Takashi Sakamoto
Browse files

firewire: core: fix race condition against transaction list



The list of transaction is enumerated without acquiring card lock when
processing AR response event. This causes a race condition bug when
processing AT request completion event concurrently.

This commit fixes the bug by put timer start for split transaction
expiration into the scope of lock. The value of jiffies in card structure
is referred before acquiring the lock.

Cc: stable@vger.kernel.org # v6.18
Fixes: b5725cfa ("firewire: core: use spin lock specific to timer for split transaction")
Reported-by: default avatarAndreas Persson <andreasp56@outlook.com>
Closes: https://github.com/alsa-project/snd-firewire-ctl-services/issues/209


Tested-by: default avatarAndreas Persson <andreasp56@outlook.com>
Link: https://lore.kernel.org/r/20260127223413.22265-1-o-takashi@sakamocchi.jp


Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
parent 63804fed
Loading
Loading
Loading
Loading
+10 −9
Original line number Diff line number Diff line
@@ -173,20 +173,14 @@ static void split_transaction_timeout_callback(struct timer_list *timer)
	}
}

static void start_split_transaction_timeout(struct fw_transaction *t,
					    struct fw_card *card)
// card->transactions.lock should be acquired in advance for the linked list.
static void start_split_transaction_timeout(struct fw_transaction *t, unsigned int delta)
{
	unsigned long delta;

	if (list_empty(&t->link) || WARN_ON(t->is_split_transaction))
		return;

	t->is_split_transaction = true;

	// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
	// local destination never runs in any type of IRQ context.
	scoped_guard(spinlock_irqsave, &card->split_timeout.lock)
		delta = card->split_timeout.jiffies;
	mod_timer(&t->split_timeout_timer, jiffies + delta);
}

@@ -207,13 +201,20 @@ static void transmit_complete_callback(struct fw_packet *packet,
		break;
	case ACK_PENDING:
	{
		unsigned int delta;

		// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
		// local destination never runs in any type of IRQ context.
		scoped_guard(spinlock_irqsave, &card->split_timeout.lock) {
			t->split_timeout_cycle =
				compute_split_timeout_timestamp(card, packet->timestamp) & 0xffff;
			delta = card->split_timeout.jiffies;
		}
		start_split_transaction_timeout(t, card);

		// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
		// local destination never runs in any type of IRQ context.
		scoped_guard(spinlock_irqsave, &card->transactions.lock)
			start_split_transaction_timeout(t, delta);
		break;
	}
	case ACK_BUSY_X: