fs/ntfs3: Fix warning in ni_fiemap

Use local runs_tree instead of cached. This way excludes rw_semaphore lock.

Reported-by: syzbot+1c25748a40fe79b8a119@syzkaller.appspotmail.com
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
Konstantin Komarov
2024-10-08 10:48:15 +03:00
parent 8e929cb546
commit e2705dd3d1
3 changed files with 21 additions and 94 deletions
+4 -5
View File
@@ -977,7 +977,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
/* Check for compressed frame. */ /* Check for compressed frame. */
err = attr_is_frame_compressed(ni, attr_b, vcn >> NTFS_LZNT_CUNIT, err = attr_is_frame_compressed(ni, attr_b, vcn >> NTFS_LZNT_CUNIT,
&hint); &hint, run);
if (err) if (err)
goto out; goto out;
@@ -1521,16 +1521,16 @@ out:
* attr_is_frame_compressed - Used to detect compressed frame. * attr_is_frame_compressed - Used to detect compressed frame.
* *
* attr - base (primary) attribute segment. * attr - base (primary) attribute segment.
* run - run to use, usually == &ni->file.run.
* Only base segments contains valid 'attr->nres.c_unit' * Only base segments contains valid 'attr->nres.c_unit'
*/ */
int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr,
CLST frame, CLST *clst_data) CLST frame, CLST *clst_data, struct runs_tree *run)
{ {
int err; int err;
u32 clst_frame; u32 clst_frame;
CLST clen, lcn, vcn, alen, slen, vcn_next; CLST clen, lcn, vcn, alen, slen, vcn_next;
size_t idx; size_t idx;
struct runs_tree *run;
*clst_data = 0; *clst_data = 0;
@@ -1542,7 +1542,6 @@ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr,
clst_frame = 1u << attr->nres.c_unit; clst_frame = 1u << attr->nres.c_unit;
vcn = frame * clst_frame; vcn = frame * clst_frame;
run = &ni->file.run;
if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) {
err = attr_load_runs_vcn(ni, attr->type, attr_name(attr), err = attr_load_runs_vcn(ni, attr->type, attr_name(attr),
@@ -1678,7 +1677,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
if (err) if (err)
goto out; goto out;
err = attr_is_frame_compressed(ni, attr_b, frame, &clst_data); err = attr_is_frame_compressed(ni, attr_b, frame, &clst_data, run);
if (err) if (err)
goto out; goto out;
+15 -88
View File
@@ -1900,46 +1900,6 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
return REPARSE_LINK; return REPARSE_LINK;
} }
/*
* fiemap_fill_next_extent_k - a copy of fiemap_fill_next_extent
* but it uses 'fe_k' instead of fieinfo->fi_extents_start
*/
static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo,
struct fiemap_extent *fe_k, u64 logical,
u64 phys, u64 len, u32 flags)
{
struct fiemap_extent extent;
/* only count the extents */
if (fieinfo->fi_extents_max == 0) {
fieinfo->fi_extents_mapped++;
return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
}
if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
return 1;
if (flags & FIEMAP_EXTENT_DELALLOC)
flags |= FIEMAP_EXTENT_UNKNOWN;
if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED)
flags |= FIEMAP_EXTENT_ENCODED;
if (flags & (FIEMAP_EXTENT_DATA_TAIL | FIEMAP_EXTENT_DATA_INLINE))
flags |= FIEMAP_EXTENT_NOT_ALIGNED;
memset(&extent, 0, sizeof(extent));
extent.fe_logical = logical;
extent.fe_physical = phys;
extent.fe_length = len;
extent.fe_flags = flags;
memcpy(fe_k + fieinfo->fi_extents_mapped, &extent, sizeof(extent));
fieinfo->fi_extents_mapped++;
if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
return 1;
return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
}
/* /*
* ni_fiemap - Helper for file_fiemap(). * ni_fiemap - Helper for file_fiemap().
* *
@@ -1950,11 +1910,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
__u64 vbo, __u64 len) __u64 vbo, __u64 len)
{ {
int err = 0; int err = 0;
struct fiemap_extent *fe_k = NULL;
struct ntfs_sb_info *sbi = ni->mi.sbi; struct ntfs_sb_info *sbi = ni->mi.sbi;
u8 cluster_bits = sbi->cluster_bits; u8 cluster_bits = sbi->cluster_bits;
struct runs_tree *run; struct runs_tree run;
struct rw_semaphore *run_lock;
struct ATTRIB *attr; struct ATTRIB *attr;
CLST vcn = vbo >> cluster_bits; CLST vcn = vbo >> cluster_bits;
CLST lcn, clen; CLST lcn, clen;
@@ -1965,13 +1923,11 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
u32 flags; u32 flags;
bool ok; bool ok;
run_init(&run);
if (S_ISDIR(ni->vfs_inode.i_mode)) { if (S_ISDIR(ni->vfs_inode.i_mode)) {
run = &ni->dir.alloc_run;
attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME, attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME,
ARRAY_SIZE(I30_NAME), NULL, NULL); ARRAY_SIZE(I30_NAME), NULL, NULL);
run_lock = &ni->dir.run_lock;
} else { } else {
run = &ni->file.run;
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL,
NULL); NULL);
if (!attr) { if (!attr) {
@@ -1986,7 +1942,6 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
"fiemap is not supported for compressed file (cp -r)"); "fiemap is not supported for compressed file (cp -r)");
goto out; goto out;
} }
run_lock = &ni->file.run_lock;
} }
if (!attr || !attr->non_res) { if (!attr || !attr->non_res) {
@@ -1998,51 +1953,33 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
goto out; goto out;
} }
/*
* To avoid lock problems replace pointer to user memory by pointer to kernel memory.
*/
fe_k = kmalloc_array(fieinfo->fi_extents_max,
sizeof(struct fiemap_extent),
GFP_NOFS | __GFP_ZERO);
if (!fe_k) {
err = -ENOMEM;
goto out;
}
end = vbo + len; end = vbo + len;
alloc_size = le64_to_cpu(attr->nres.alloc_size); alloc_size = le64_to_cpu(attr->nres.alloc_size);
if (end > alloc_size) if (end > alloc_size)
end = alloc_size; end = alloc_size;
down_read(run_lock);
while (vbo < end) { while (vbo < end) {
if (idx == -1) { if (idx == -1) {
ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);
} else { } else {
CLST vcn_next = vcn; CLST vcn_next = vcn;
ok = run_get_entry(run, ++idx, &vcn, &lcn, &clen) && ok = run_get_entry(&run, ++idx, &vcn, &lcn, &clen) &&
vcn == vcn_next; vcn == vcn_next;
if (!ok) if (!ok)
vcn = vcn_next; vcn = vcn_next;
} }
if (!ok) { if (!ok) {
up_read(run_lock);
down_write(run_lock);
err = attr_load_runs_vcn(ni, attr->type, err = attr_load_runs_vcn(ni, attr->type,
attr_name(attr), attr_name(attr),
attr->name_len, run, vcn); attr->name_len, &run, vcn);
up_write(run_lock);
down_read(run_lock);
if (err) if (err)
break; break;
ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);
if (!ok) { if (!ok) {
err = -EINVAL; err = -EINVAL;
@@ -2067,8 +2004,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
} else if (is_attr_compressed(attr)) { } else if (is_attr_compressed(attr)) {
CLST clst_data; CLST clst_data;
err = attr_is_frame_compressed( err = attr_is_frame_compressed(ni, attr,
ni, attr, vcn >> attr->nres.c_unit, &clst_data); vcn >> attr->nres.c_unit,
&clst_data, &run);
if (err) if (err)
break; break;
if (clst_data < NTFS_LZNT_CLUSTERS) if (clst_data < NTFS_LZNT_CLUSTERS)
@@ -2097,8 +2035,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
if (vbo + dlen >= end) if (vbo + dlen >= end)
flags |= FIEMAP_EXTENT_LAST; flags |= FIEMAP_EXTENT_LAST;
err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo, err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen,
dlen, flags); flags);
if (err < 0) if (err < 0)
break; break;
@@ -2119,8 +2057,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
if (vbo + bytes >= end) if (vbo + bytes >= end)
flags |= FIEMAP_EXTENT_LAST; flags |= FIEMAP_EXTENT_LAST;
err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo, bytes, err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags);
flags);
if (err < 0) if (err < 0)
break; break;
if (err == 1) { if (err == 1) {
@@ -2131,19 +2068,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
vbo += bytes; vbo += bytes;
} }
up_read(run_lock);
/*
* Copy to user memory out of lock
*/
if (copy_to_user(fieinfo->fi_extents_start, fe_k,
fieinfo->fi_extents_max *
sizeof(struct fiemap_extent))) {
err = -EFAULT;
}
out: out:
kfree(fe_k); run_close(&run);
return err; return err;
} }
@@ -2672,7 +2598,8 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
down_write(&ni->file.run_lock); down_write(&ni->file.run_lock);
run_truncate_around(run, le64_to_cpu(attr->nres.svcn)); run_truncate_around(run, le64_to_cpu(attr->nres.svcn));
frame = frame_vbo >> (cluster_bits + NTFS_LZNT_CUNIT); frame = frame_vbo >> (cluster_bits + NTFS_LZNT_CUNIT);
err = attr_is_frame_compressed(ni, attr, frame, &clst_data); err = attr_is_frame_compressed(ni, attr, frame, &clst_data,
run);
up_write(&ni->file.run_lock); up_write(&ni->file.run_lock);
if (err) if (err)
goto out1; goto out1;
+2 -1
View File
@@ -446,7 +446,8 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
struct runs_tree *run, u64 frame, u64 frames, struct runs_tree *run, u64 frame, u64 frames,
u8 frame_bits, u32 *ondisk_size, u64 *vbo_data); u8 frame_bits, u32 *ondisk_size, u64 *vbo_data);
int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr,
CLST frame, CLST *clst_data); CLST frame, CLST *clst_data,
struct runs_tree *run);
int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
u64 new_valid); u64 new_valid);
int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes);