CVE-2024-27080
Publication date:
01/05/2024
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
btrfs: fix race when detecting delalloc ranges during fiemap<br />
<br />
For fiemap we recently stopped locking the target extent range for the<br />
whole duration of the fiemap call, in order to avoid a deadlock in a<br />
scenario where the fiemap buffer happens to be a memory mapped range of<br />
the same file. This use case is very unlikely to be useful in practice but<br />
it may be triggered by fuzz testing (syzbot, etc).<br />
<br />
This however introduced a race that makes us miss delalloc ranges for<br />
file regions that are currently holes, so the caller of fiemap will not<br />
be aware that there&#39;s data for some file regions. This can be quite<br />
serious for some use cases - for example in coreutils versions before 9.0,<br />
the cp program used fiemap to detect holes and data in the source file,<br />
copying only regions with data (extents or delalloc) from the source file<br />
to the destination file in order to preserve holes (see the documentation<br />
for its --sparse command line option). This means that if cp was used<br />
with a source file that had delalloc in a hole, the destination file could<br />
end up without that data, which is effectively a data loss issue, if it<br />
happened to hit the race described below.<br />
<br />
The race happens like this:<br />
<br />
1) Fiemap is called, without the FIEMAP_FLAG_SYNC flag, for a file that<br />
has delalloc in the file range [64M, 65M[, which is currently a hole;<br />
<br />
2) Fiemap locks the inode in shared mode, then starts iterating the<br />
inode&#39;s subvolume tree searching for file extent items, without having<br />
the whole fiemap target range locked in the inode&#39;s io tree - the<br />
change introduced recently by commit b0ad381fa769 ("btrfs: fix<br />
deadlock with fiemap and extent locking"). It only locks ranges in<br />
the io tree when it finds a hole or prealloc extent since that<br />
commit;<br />
<br />
3) Note that fiemap clones each leaf before using it, and this is to<br />
avoid deadlocks when locking a file range in the inode&#39;s io tree and<br />
the fiemap buffer is memory mapped to some file, because writing<br />
to the page with btrfs_page_mkwrite() will wait on any ordered extent<br />
for the page&#39;s range and the ordered extent needs to lock the range<br />
and may need to modify the same leaf, therefore leading to a deadlock<br />
on the leaf;<br />
<br />
4) While iterating the file extent items in the cloned leaf before<br />
finding the hole in the range [64M, 65M[, the delalloc in that range<br />
is flushed and its ordered extent completes - meaning the corresponding<br />
file extent item is in the inode&#39;s subvolume tree, but not present in<br />
the cloned leaf that fiemap is iterating over;<br />
<br />
5) When fiemap finds the hole in the [64M, 65M[ range by seeing the gap in<br />
the cloned leaf (or a file extent item with disk_bytenr == 0 in case<br />
the NO_HOLES feature is not enabled), it will lock that file range in<br />
the inode&#39;s io tree and then search for delalloc by checking for the<br />
EXTENT_DELALLOC bit in the io tree for that range and ordered extents<br />
(with btrfs_find_delalloc_in_range()). But it finds nothing since the<br />
delalloc in that range was already flushed and the ordered extent<br />
completed and is gone - as a result fiemap will not report that there&#39;s<br />
delalloc or an extent for the range [64M, 65M[, so user space will be<br />
mislead into thinking that there&#39;s a hole in that range.<br />
<br />
This could actually be sporadically triggered with test case generic/094<br />
from fstests, which reports a missing extent/delalloc range like this:<br />
<br />
generic/094 2s ... - output mismatch (see /home/fdmanana/git/hub/xfstests/results//generic/094.out.bad)<br />
--- tests/generic/094.out 2020-06-10 19:29:03.830519425 +0100<br />
+++ /home/fdmanana/git/hub/xfstests/results//generic/094.out.bad 2024-02-28 11:00:00.381071525 +0000<br />
@@ -1,3 +1,9 @@<br />
QA output created by 094<br />
fiemap run with sync<br />
fiemap run without sync<br />
+ERROR: couldn&#39;t find extent at 7<br />
+map is &#39;HHDDHPPDPHPH&#39;<br />
+logical: [ 5.. 6] phys:<br />
---truncated---
Severity CVSS v4.0: Pending analysis
Last modification:
18/09/2025