Re: [PATCH v4 2/4] ntfs: protect attribute-list buffer replacement with attr_list_persist_lock
Namjae Jeon <[email protected]> Fri, 31 Jul 2026 22:56:15 +0900
| Newsgroups | dev.linux.lists.ntfs |
|---|---|
| Message-ID | <CAKYAXd8sQyEjnh69qdOX_-jaP-6ViN0K4YibhaPH+MC4-18QVg@mail.gmail.com> |
> +int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni)
> {
> struct inode *attr_vi;
> struct ntfs_inode *attr_ni;
> @@ -111,6 +117,23 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
> return 0;
> }
Thread A and Thread B are serialized by different locks here.
Thread A holds attr_list_persist_lock and enters
ntfs_attrlist_update_locked(). It passes base_ni->attr_list directly
to
ntfs_inode_attr_pwrite(), but it does not hold attr_list_lock while
that buffer is being read and written to disk.
At the same time, Thread B can enter ntfs_attr_record_move_to(). That
path takes only attr_list_lock for write, so it is not blocked by
Thread A's attr_list_persist_lock. Thread B can therefore change an
ALE's mft_reference and instance while Thread A is still copying the
same attr_list buffer to disk.
The on-disk attribute list can then contain stale or partially updated
extent identity information, even though the corresponding attribute
record has already been moved.
The mutation and the persist need to belong to the same transaction.
Either make every in-place ALE modifier hold attr_list_persist_lock
until its update has been persisted, or take a private copy of
attr_list under attr_list_lock and persist that snapshot instead of
the live buffer.
Thread A: persist transaction Thread B: record move
------------------------------------------- --------------------------------
1. Takes attr_list_persist_lock
2. Starts ntfs_attrlist_update_locked()
3. Passes base_ni->attr_list directly
to ntfs_inode_attr_pwrite()
(does NOT hold attr_list_lock)
4. Takes
attr_list_lock for write
This succeeds
because Thread A
does not hold its read side.
5. Changes ALE:
ale->mft_reference = ...
ale->instance = ...
6. Releases attr_list_lock
7. Continues copying the same buffer to disk