Re: [PATCH v2] ntfs: hold the runlist lock while expanding a non-resident attribute

From: Namjae Jeon

Date: Fri Oct 02 2026 - 20:42:07 EST


On Fri, Oct 2, 2026 at 12:06 AM Matthias Goergens
<matthias.goergens@xxxxxxxxx> wrote:
>
> ntfs_non_resident_attr_expand() changes ni->runlist.rl while it
> allocates clusters: ntfs_attr_map_whole_runlist(), the compressed
> branch's ntfs_rl_realloc() and ntfs_runlists_merge() can each kvfree()
> the old array. Unless the caller already holds ni->runlist.lock, none
> of this happens under it; only the rollback takes it. Callers without
> it include the write path, fallocate, ntfs_inode_attr_pwrite() and
> ntfs_resident_attr_resize().
>
> Those callers hold mrec_lock, and the write path also inode_lock, but
> the iomap read side takes neither: FIEMAP, page faults, readahead and
> splice read reach ntfs_read_iomap_begin_non_resident(), which takes only
> ni->runlist.lock. A FIEMAP running while write(2) extends the same file
> can walk the array that ntfs_runlists_merge() has just freed:
>
> BUG: KASAN: slab-use-after-free in ntfs_attr_vcn_to_rl+0x19b/0x200
>
> A reader that finds a vcn unmapped in the stale array also maps it from
> disk into the runlist the writer is changing, which shows up as "Run
> lists overlap. Cannot merge!".
>
> Take ni->runlist.lock for writing around the part of the expansion that
> changes the runlist and allocated_size, up to and including the mapping
> pairs update, as ntfs_non_resident_attr_shrink() does since commit
> 91709ba5d6d7 ("ntfs: protect runlist updates with the runlist lock"),
> and hold it throughout the rollback, where ntfs_cluster_free() requires
> it. The lock nests inside mrec_lock, as on the truncate-up path, which
> already calls this function with the runlist lock held. Where the
> caller says it holds the lock (locked_ni == ni), assert that it holds it
> for writing.
>
> For an $ATTRIBUTE_LIST whose lock the caller does not hold, drop the
> lock again before the mapping pairs update: that update can resize the
> same list through ntfs_attrlist_update_locked(), which returns -ENOSPC
> when told that the list's lock is held. Holding it there made punching
> holes into a large fragmented file fail with -ENOSPC.
>
> Fixes: 495e90fa3348 ("ntfs: update attrib operations")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Matthias Goergens <matthias.goergens@xxxxxxxxx>
> Reviewed-by: Baolin Liu <liubaolin@xxxxxxxxxx>
> Reviewed-by: Hyunchul Lee <hyc.lee@xxxxxxxxx>
Applied it to #ntfs-next.
Thanks!