Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: b5725cfa4120 ("firewire: core: use spin lock specific to timer for split transaction")
Reported-by: Andreas Persson <andreasp56@outlook.com>
Closes: https://github.com/alsa-project/snd-firewire-ctl-services/issues/209
Tested-by: Andreas Persson <andreasp56@outlook.com>
Link: https://lore.kernel.org/r/20260127223413.22265-1-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>

+10 -9
+10 -9
drivers/firewire/core-transaction.c
··· 173 173 } 174 174 } 175 175 176 - static void start_split_transaction_timeout(struct fw_transaction *t, 177 - struct fw_card *card) 176 + // card->transactions.lock should be acquired in advance for the linked list. 177 + static void start_split_transaction_timeout(struct fw_transaction *t, unsigned int delta) 178 178 { 179 - unsigned long delta; 180 - 181 179 if (list_empty(&t->link) || WARN_ON(t->is_split_transaction)) 182 180 return; 183 181 184 182 t->is_split_transaction = true; 185 183 186 - // NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for 187 - // local destination never runs in any type of IRQ context. 188 - scoped_guard(spinlock_irqsave, &card->split_timeout.lock) 189 - delta = card->split_timeout.jiffies; 190 184 mod_timer(&t->split_timeout_timer, jiffies + delta); 191 185 } 192 186 ··· 201 207 break; 202 208 case ACK_PENDING: 203 209 { 210 + unsigned int delta; 211 + 204 212 // NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for 205 213 // local destination never runs in any type of IRQ context. 206 214 scoped_guard(spinlock_irqsave, &card->split_timeout.lock) { 207 215 t->split_timeout_cycle = 208 216 compute_split_timeout_timestamp(card, packet->timestamp) & 0xffff; 217 + delta = card->split_timeout.jiffies; 209 218 } 210 - start_split_transaction_timeout(t, card); 219 + 220 + // NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for 221 + // local destination never runs in any type of IRQ context. 222 + scoped_guard(spinlock_irqsave, &card->transactions.lock) 223 + start_split_transaction_timeout(t, delta); 211 224 break; 212 225 } 213 226 case ACK_BUSY_X: