[PATCH v2 4/4] ntfs: protect mapping-pairs attr-list updates with attr_list_persist_lock
Hyunchul Lee <[email protected]> Tue, 28 Jul 2026 09:30:51 +0900
| Newsgroups | dev.linux.lists.ntfs,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
ntfs_attr_update_mapping_pairs() updates several ALEs while rebuilding a fragmented attribute's mapping pairs and persists the attribute list once after the loop. Each ALE update is covered by attr_list_lock, but the overall publish/persist sequence is not serialized against concurrent ntfs_attrlist_entry_add()/rm() on the same base inode. Acquire attr_list_persist_lock once after resolving base_ni whenever the call can touch the attribute list, and hold it across every ALE update and the deferred ntfs_attrlist_update() call. Reported-by: Cen Zhang <[email protected]> Link: https://lore.kernel.org/all/[email protected]/ Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: [email protected] Tested-by: Cen Zhang <[email protected]> Signed-off-by: Hyunchul Lee <[email protected]> --- fs/ntfs/attrib.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 00ff223d9c21..85736af66bb1 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -3887,7 +3887,7 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) struct attr_record *a; s64 stop_vcn; int err = 0, mp_size, cur_max_mp_size, exp_max_mp_size; - bool finished_build, attrlist_changed = false; + bool finished_build, attrlist_changed = false, attrlist_locked = false; bool first_updated = false; struct super_block *sb; struct runlist_element *start_rl; @@ -3912,9 +3912,16 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) else base_ni = ni; + if (NInoAttrList(base_ni) && ni->type != AT_ATTRIBUTE_LIST) { + mutex_lock(&base_ni->attr_list_persist_lock); + attrlist_locked = true; + } + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); if (!ctx) { ntfs_error(sb, "%s: Failed to get search context", __func__); + if (attrlist_locked) + mutex_unlock(&base_ni->attr_list_persist_lock); return -ENOMEM; } @@ -3989,6 +3996,10 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) err = ntfs_attr_update_meta(a, ni, m, ctx); if (err < 0) { if (err == -EAGAIN) { + if (attrlist_locked) { + mutex_unlock(&base_ni->attr_list_persist_lock); + attrlist_locked = false; + } ntfs_attr_put_search_ctx(ctx); goto retry; } @@ -4025,6 +4036,10 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) * attributes and try again. */ if (ni->type == AT_ATTRIBUTE_LIST) { + if (WARN_ON_ONCE(attrlist_locked)) { + mutex_unlock(&base_ni->attr_list_persist_lock); + attrlist_locked = false; + } ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_free_space(base_ni, mp_size - cur_max_mp_size)) { @@ -4038,6 +4053,10 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) /* Add attribute list if it isn't present, and retry. */ if (!NInoAttrList(base_ni)) { + if (WARN_ON_ONCE(attrlist_locked)) { + mutex_unlock(&base_ni->attr_list_persist_lock); + attrlist_locked = false; + } ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_add_attrlist(base_ni)) { ntfs_error(sb, "Can not add attrlist"); @@ -4151,9 +4170,13 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) if (attrlist_changed) { err = ntfs_attrlist_update(base_ni); - if (err) - goto put_err_out; } + if (attrlist_locked) { + mutex_unlock(&base_ni->attr_list_persist_lock); + attrlist_locked = false; + } + if (attrlist_changed && err) + goto put_err_out; /* Deallocate not used attribute extents and return with success. */ if (finished_build) { @@ -4272,6 +4295,8 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) return 0; put_err_out: + if (attrlist_locked) + mutex_unlock(&base_ni->attr_list_persist_lock); if (ctx) ntfs_attr_put_search_ctx(ctx); return err; -- 2.43.0