dm integrity: fix a crash due to BUG_ON in __journal_read_write()
Fix a crash that was introduced by the commit724376a04d
. The crash is reported here: https://gitlab.com/cryptsetup/cryptsetup/issues/468 When reading from the integrity device, the function dm_integrity_map_continue calls find_journal_node to find out if the location to read is present in the journal. Then, it calculates how many sectors are consecutively stored in the journal. Then, it locks the range with add_new_range and wait_and_add_new_range. The problem is that during wait_and_add_new_range, we hold no locks (we don't hold ic->endio_wait.lock and we don't hold a range lock), so the journal may change arbitrarily while wait_and_add_new_range sleeps. The code then goes to __journal_read_write and hits BUG_ON(journal_entry_get_sector(je) != logical_sector); because the journal has changed. In order to fix this bug, we need to re-check the journal location after wait_and_add_new_range. We restrict the length to one block in order to not complicate the code too much. Fixes:724376a04d
("dm integrity: implement fair range locks") Cc: stable@vger.kernel.org # v4.19+ Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
Родитель
ad1bd578bd
Коммит
5729b6e5a1
|
@ -1943,7 +1943,22 @@ offload_to_thread:
|
||||||
queue_work(ic->wait_wq, &dio->work);
|
queue_work(ic->wait_wq, &dio->work);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (journal_read_pos != NOT_FOUND)
|
||||||
|
dio->range.n_sectors = ic->sectors_per_block;
|
||||||
wait_and_add_new_range(ic, &dio->range);
|
wait_and_add_new_range(ic, &dio->range);
|
||||||
|
/*
|
||||||
|
* wait_and_add_new_range drops the spinlock, so the journal
|
||||||
|
* may have been changed arbitrarily. We need to recheck.
|
||||||
|
* To simplify the code, we restrict I/O size to just one block.
|
||||||
|
*/
|
||||||
|
if (journal_read_pos != NOT_FOUND) {
|
||||||
|
sector_t next_sector;
|
||||||
|
unsigned new_pos = find_journal_node(ic, dio->range.logical_sector, &next_sector);
|
||||||
|
if (unlikely(new_pos != journal_read_pos)) {
|
||||||
|
remove_range_unlocked(ic, &dio->range);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&ic->endio_wait.lock);
|
spin_unlock_irq(&ic->endio_wait.lock);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче