Skip to content

Commit 88ca252

Browse files
heynemaxbostrovs
authored andcommitted
xen/events: Fix race in set_evtchn_to_irq
There is a TOCTOU issue in set_evtchn_to_irq. Rows in the evtchn_to_irq mapping are lazily allocated in this function. The check whether the row is already present and the row initialization is not synchronized. Two threads can at the same time allocate a new row for evtchn_to_irq and add the irq mapping to the their newly allocated row. One thread will overwrite what the other has set for evtchn_to_irq[row] and therefore the irq mapping is lost. This will trigger a BUG_ON later in bind_evtchn_to_cpu: INFO: pci 0000:1a:15.4: [1d0f:8061] type 00 class 0x010802 INFO: nvme 0000:1a:12.1: enabling device (0000 -> 0002) INFO: nvme nvme77: 1/0/0 default/read/poll queues CRIT: kernel BUG at drivers/xen/events/events_base.c:427! WARN: invalid opcode: 0000 [#1] SMP NOPTI WARN: Workqueue: nvme-reset-wq nvme_reset_work [nvme] WARN: RIP: e030:bind_evtchn_to_cpu+0xc2/0xd0 WARN: Call Trace: WARN: set_affinity_irq+0x121/0x150 WARN: irq_do_set_affinity+0x37/0xe0 WARN: irq_setup_affinity+0xf6/0x170 WARN: irq_startup+0x64/0xe0 WARN: __setup_irq+0x69e/0x740 WARN: ? request_threaded_irq+0xad/0x160 WARN: request_threaded_irq+0xf5/0x160 WARN: ? nvme_timeout+0x2f0/0x2f0 [nvme] WARN: pci_request_irq+0xa9/0xf0 WARN: ? pci_alloc_irq_vectors_affinity+0xbb/0x130 WARN: queue_request_irq+0x4c/0x70 [nvme] WARN: nvme_reset_work+0x82d/0x1550 [nvme] WARN: ? check_preempt_wakeup+0x14f/0x230 WARN: ? check_preempt_curr+0x29/0x80 WARN: ? nvme_irq_check+0x30/0x30 [nvme] WARN: process_one_work+0x18e/0x3c0 WARN: worker_thread+0x30/0x3a0 WARN: ? process_one_work+0x3c0/0x3c0 WARN: kthread+0x113/0x130 WARN: ? kthread_park+0x90/0x90 WARN: ret_from_fork+0x3a/0x50 This patch sets evtchn_to_irq rows via a cmpxchg operation so that they will be set only once. The row is now cleared before writing it to evtchn_to_irq in order to not create a race once the row is visible for other threads. While at it, do not require the page to be zeroed, because it will be overwritten with -1's in clear_evtchn_to_irq_row anyway. Signed-off-by: Maximilian Heyne <[email protected]> Fixes: d0b075f ("xen/events: Refactor evtchn_to_irq array to be dynamically allocated") Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Boris Ostrovsky <[email protected]> Signed-off-by: Boris Ostrovsky <[email protected]>
1 parent 83f877a commit 88ca252

File tree

1 file changed

+14
-6
lines changed

1 file changed

+14
-6
lines changed

drivers/xen/events/events_base.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,12 @@ static void disable_dynirq(struct irq_data *data);
198198

199199
static DEFINE_PER_CPU(unsigned int, irq_epoch);
200200

201-
static void clear_evtchn_to_irq_row(unsigned row)
201+
static void clear_evtchn_to_irq_row(int *evtchn_row)
202202
{
203203
unsigned col;
204204

205205
for (col = 0; col < EVTCHN_PER_ROW; col++)
206-
WRITE_ONCE(evtchn_to_irq[row][col], -1);
206+
WRITE_ONCE(evtchn_row[col], -1);
207207
}
208208

209209
static void clear_evtchn_to_irq_all(void)
@@ -213,14 +213,15 @@ static void clear_evtchn_to_irq_all(void)
213213
for (row = 0; row < EVTCHN_ROW(xen_evtchn_max_channels()); row++) {
214214
if (evtchn_to_irq[row] == NULL)
215215
continue;
216-
clear_evtchn_to_irq_row(row);
216+
clear_evtchn_to_irq_row(evtchn_to_irq[row]);
217217
}
218218
}
219219

220220
static int set_evtchn_to_irq(evtchn_port_t evtchn, unsigned int irq)
221221
{
222222
unsigned row;
223223
unsigned col;
224+
int *evtchn_row;
224225

225226
if (evtchn >= xen_evtchn_max_channels())
226227
return -EINVAL;
@@ -233,11 +234,18 @@ static int set_evtchn_to_irq(evtchn_port_t evtchn, unsigned int irq)
233234
if (irq == -1)
234235
return 0;
235236

236-
evtchn_to_irq[row] = (int *)get_zeroed_page(GFP_KERNEL);
237-
if (evtchn_to_irq[row] == NULL)
237+
evtchn_row = (int *) __get_free_pages(GFP_KERNEL, 0);
238+
if (evtchn_row == NULL)
238239
return -ENOMEM;
239240

240-
clear_evtchn_to_irq_row(row);
241+
clear_evtchn_to_irq_row(evtchn_row);
242+
243+
/*
244+
* We've prepared an empty row for the mapping. If a different
245+
* thread was faster inserting it, we can drop ours.
246+
*/
247+
if (cmpxchg(&evtchn_to_irq[row], NULL, evtchn_row) != NULL)
248+
free_page((unsigned long) evtchn_row);
241249
}
242250

243251
WRITE_ONCE(evtchn_to_irq[row][col], irq);

0 commit comments

Comments
 (0)