]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()
authorDeepanshu Kartikey <kartikey406@gmail.com>
Thu, 19 Mar 2026 07:45:46 +0000 (13:15 +0530)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Tue, 24 Mar 2026 17:51:28 +0000 (18:51 +0100)
When a compressed or sparse attribute has its clusters frame-aligned,
vcn is rounded down to the frame start using cmask, which can result
in vcn != vcn0. In this case, vcn and vcn0 may reside in different
attribute segments.

The code already handles the case where vcn is in a different segment
by loading its runs before allocation. However, it fails to load runs
for vcn0 when vcn0 resides in a different segment than vcn. This causes
run_lookup_entry() to return SPARSE_LCN for vcn0 since its segment was
never loaded into the in-memory run list, triggering the WARN_ON(1).

Fix this by adding a missing check for vcn0 after the existing vcn
segment check. If vcn0 falls outside the current segment range
[svcn, evcn1), find and load the attribute segment containing vcn0
before performing the run lookup.

The following scenario triggers the bug:
  attr_data_get_block_locked()
    vcn = vcn0 & cmask        <- vcn != vcn0 after frame alignment
    load runs for vcn segment <- vcn0 segment not loaded!
    attr_allocate_clusters()  <- allocation succeeds
    run_lookup_entry(vcn0)    <- vcn0 not in run -> SPARSE_LCN
    WARN_ON(1)                <- bug fires here!

Reported-by: syzbot+c1e9aedbd913fadad617@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c1e9aedbd913fadad617
Fixes: c380b52f6c57 ("fs/ntfs3: Change new sparse cluster processing")
Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
fs/ntfs3/attrib.c

index 6cb9bc5d605c268a9d8a05a9524529de864e09ed..76e581d3961d054228f19cc6f4873bec8bf1c997 100644 (file)
@@ -1152,6 +1152,21 @@ again:
                        if (err)
                                goto out;
                }
+
+               if (vcn0 < svcn || evcn1 <= vcn0) {
+                       struct ATTRIB *attr2;
+
+                       attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
+                                              0, &vcn0, &mi);
+                       if (!attr2) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       err = attr_load_runs(attr2, ni, run, NULL);
+                       if (err)
+                               goto out;
+               }
+
                da = false; /* no delalloc for compressed file. */
        }