mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
Description for this pull request:
- Fix a NULL pointer dereference in ntfs_index_walk_down() by validating
index block allocation.
- Fix a memory leak of the symlink target string in
ntfs_reparse_set_wsl_symlink() during error paths.
- Prevent VCN overflow and validate lowest_vcn in
ntfs_mapping_pairs_decompress() to avoid runlist corruption.
- Fix a page reference leak in ntfs_write_iomap_end_resident() when
attribute search context allocation fails.
- Fix an invalid PTR_ERR() usage on a valid folio pointer in
__ntfs_bitmap_set_bits_in_run().
- Correct directory link counting by dropping nlink only when the MFT
record link count reaches zero for WIN32/DOS aliases.
- Fix an uninitialized variable usage in ntfs_mapping_pairs_decompress()
by returning an error pointer directly.
-----BEGIN PGP SIGNATURE-----
iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmn1SX4WHGxpbmtpbmpl
b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCLFtEACQou87tSAG0pjuOe4FDW2/ijTJ
B4CWQ5AxSU/G8Mts1Or9bvjKMA2zI8A/N8Bx0kzZviB8G1TiIs2y8KWqJajLCXsX
dEvLwu1UUvtYlclw3sVdo+7oA8lB9NQB5LNlaubTzkDeCXHpkfQ5/+zgbU2Bdpjf
5qe34klrr8jU6KHIJnQlpiqJj8wYvNXizDRYkYZw0tMzNGlzM5csO8cZ4HNW8ENK
+D7CAKBDW4JA8AaaBC9eGL3cpl/a8a1X46O1LoEoCeH14FKGEGAoSa5z5aWBDJpg
X84v/19iP9Ti2poh2I5KZZfgKxFjsQodXYoPRofrXCGpVYUveTRmfEZ//qt33mr/
Y+bX5iTBjP0H4OLr5o8TZNgHXqjsR5/kkbnz71VEZey53U3/fFLC6L0tt9S9vLnb
mC2YghFgmcgQEIYz3S79F8K0JBEl4gSUsMNQtM8+vjqpYRsqFSSUYSEUqEJWgdaK
1tnzbZlGMTgiiNO5EdqZXLIGqsJsckUfi0Qr3tnzdw2CWqj6Q0fCbBV0KVfeLYuY
LtFfG6W2A8KUAvX+Nc6+MiQ887A9F8VYjR4sIC633IISiU05Kfd3OWP4Bx+05Yty
wt6cccm+gCMBVMVacRDccfK+ovIDN50r+7Flbuw1jw28rxcbe5tVmoKrC3HQ/RYr
hIXUXwqqCX5VMxsAOA==
=MosZ
-----END PGP SIGNATURE-----
Merge tag 'ntfs-for-7.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs
Pull ntfs fixes from Namjae Jeon:
- Fix a NULL pointer dereference in ntfs_index_walk_down() by
validating index block allocation
- Fix a memory leak of the symlink target string in
ntfs_reparse_set_wsl_symlink() during error paths
- Prevent VCN overflow and validate lowest_vcn in
ntfs_mapping_pairs_decompress() to avoid runlist corruption
- Fix a page reference leak in ntfs_write_iomap_end_resident()
when attribute search context allocation fails
- Fix an invalid PTR_ERR() usage on a valid folio pointer in
__ntfs_bitmap_set_bits_in_run()
- Correct directory link counting by dropping nlink only when
the MFT record link count reaches zero for WIN32/DOS aliases
- Fix an uninitialized variable in ntfs_mapping_pairs_decompress()
by returning an error pointer directly
* tag 'ntfs-for-7.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs:
ntfs: Use return instead of goto in ntfs_mapping_pairs_decompress()
ntfs: drop nlink once for WIN32/DOS aliases
ntfs: fix invalid PTR_ERR() usage in __ntfs_bitmap_set_bits_in_run()
ntfs: fix error handling in ntfs_write_iomap_end_resident()
ntfs: fix VCN overflow in ntfs_mapping_pairs_decompress()
ntfs: fix WSL symlink target leak on reparse failure
ntfs: fix NULL dereference in ntfs_index_walk_down()
This commit is contained in:
commit
4c2ed2a3db
|
|
@ -125,7 +125,7 @@ int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
|
|||
struct address_space *mapping;
|
||||
struct folio *folio;
|
||||
u8 *kaddr;
|
||||
int pos, len;
|
||||
int pos, len, err;
|
||||
u8 bit;
|
||||
struct ntfs_inode *ni = NTFS_I(vi);
|
||||
struct ntfs_volume *vol = ni->vol;
|
||||
|
|
@ -201,8 +201,10 @@ int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
|
|||
|
||||
/* If we are not in the last page, deal with all subsequent pages. */
|
||||
while (index < end_index) {
|
||||
if (cnt <= 0)
|
||||
if (cnt <= 0) {
|
||||
err = -EIO;
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
/* Update @index and get the next folio. */
|
||||
folio_mark_dirty(folio);
|
||||
|
|
@ -214,6 +216,7 @@ int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
|
|||
ntfs_error(vi->i_sb,
|
||||
"Failed to map subsequent page (error %li), aborting.",
|
||||
PTR_ERR(folio));
|
||||
err = PTR_ERR(folio);
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
|
|
@ -265,7 +268,7 @@ int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
|
|||
* - @count - @cnt is the number of bits that have been modified
|
||||
*/
|
||||
if (is_rollback)
|
||||
return PTR_ERR(folio);
|
||||
return err;
|
||||
if (count != cnt)
|
||||
pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt,
|
||||
value ? 0 : 1, true);
|
||||
|
|
@ -274,14 +277,14 @@ int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
|
|||
if (!pos) {
|
||||
/* Rollback was successful. */
|
||||
ntfs_error(vi->i_sb,
|
||||
"Failed to map subsequent page (error %li), aborting.",
|
||||
PTR_ERR(folio));
|
||||
"Failed to map subsequent page (error %i), aborting.",
|
||||
err);
|
||||
} else {
|
||||
/* Rollback failed. */
|
||||
ntfs_error(vi->i_sb,
|
||||
"Failed to map subsequent page (error %li) and rollback failed (error %i). Aborting and leaving inconsistent metadata. Unmount and run chkdsk.",
|
||||
PTR_ERR(folio), pos);
|
||||
"Failed to map subsequent page (error %i) and rollback failed (error %i). Aborting and leaving inconsistent metadata. Unmount and run chkdsk.",
|
||||
err, pos);
|
||||
NVolSetErrors(NTFS_SB(vi->i_sb));
|
||||
}
|
||||
return PTR_ERR(folio);
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -911,8 +911,8 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor)
|
|||
|
||||
if (next->flags & INDEX_ENTRY_NODE) {
|
||||
next = ntfs_index_walk_down(next, ictx);
|
||||
if (!next) {
|
||||
err = -EIO;
|
||||
if (IS_ERR(next)) {
|
||||
err = PTR_ERR(next);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
@ -920,7 +920,14 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor)
|
|||
if (next && !(next->flags & INDEX_ENTRY_END))
|
||||
goto nextdir;
|
||||
|
||||
while ((next = ntfs_index_next(next, ictx)) != NULL) {
|
||||
while (1) {
|
||||
next = ntfs_index_next(next, ictx);
|
||||
if (IS_ERR(next)) {
|
||||
err = PTR_ERR(next);
|
||||
goto out;
|
||||
}
|
||||
if (!next)
|
||||
break;
|
||||
nextdir:
|
||||
/* Check the consistency of an index entry */
|
||||
if (ntfs_index_entry_inconsistent(ictx, vol, next, COLLATION_FILE_NAME,
|
||||
|
|
|
|||
|
|
@ -1969,15 +1969,19 @@ int ntfs_index_remove(struct ntfs_inode *dir_ni, const void *key, const u32 keyl
|
|||
struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_index_context *ictx)
|
||||
{
|
||||
struct index_entry *entry;
|
||||
struct index_block *ib;
|
||||
s64 vcn;
|
||||
|
||||
entry = ie;
|
||||
do {
|
||||
vcn = ntfs_ie_get_vcn(entry);
|
||||
if (ictx->is_in_root) {
|
||||
ib = kvzalloc(ictx->block_size, GFP_NOFS);
|
||||
if (!ib)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
/* down from level zero */
|
||||
ictx->ir = NULL;
|
||||
ictx->ib = kvzalloc(ictx->block_size, GFP_NOFS);
|
||||
ictx->ib = ib;
|
||||
ictx->pindex = 1;
|
||||
ictx->is_in_root = false;
|
||||
} else {
|
||||
|
|
@ -1991,8 +1995,8 @@ struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_ind
|
|||
ictx->entry = ntfs_ie_get_first(&ictx->ib->index);
|
||||
entry = ictx->entry;
|
||||
} else
|
||||
entry = NULL;
|
||||
} while (entry && (entry->flags & INDEX_ENTRY_NODE));
|
||||
entry = ERR_PTR(-EIO);
|
||||
} while (!IS_ERR(entry) && (entry->flags & INDEX_ENTRY_NODE));
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
|
@ -2097,10 +2101,15 @@ struct index_entry *ntfs_index_next(struct index_entry *ie, struct ntfs_index_co
|
|||
|
||||
/* walk down if it has a subnode */
|
||||
if (flags & INDEX_ENTRY_NODE) {
|
||||
if (!ictx->ia_ni)
|
||||
if (!ictx->ia_ni) {
|
||||
ictx->ia_ni = ntfs_ia_open(ictx, ictx->idx_ni);
|
||||
if (!ictx->ia_ni)
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
next = ntfs_index_walk_down(next, ictx);
|
||||
if (IS_ERR(next))
|
||||
return next;
|
||||
} else {
|
||||
|
||||
/* walk up it has no subnode, nor data */
|
||||
|
|
|
|||
|
|
@ -788,8 +788,7 @@ static int ntfs_write_iomap_end_resident(struct inode *inode, loff_t pos,
|
|||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
written = -ENOMEM;
|
||||
mutex_unlock(&ni->mrec_lock);
|
||||
return written;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
|
||||
|
|
@ -810,7 +809,8 @@ static int ntfs_write_iomap_end_resident(struct inode *inode, loff_t pos,
|
|||
memcpy(kattr + pos, iomap_inline_data(iomap, pos), written);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
err_out:
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
put_page(ipage);
|
||||
mutex_unlock(&ni->mrec_lock);
|
||||
return written;
|
||||
|
|
|
|||
|
|
@ -945,7 +945,8 @@ static int ntfs_delete(struct ntfs_inode *ni, struct ntfs_inode *dir_ni,
|
|||
|
||||
ni_mrec = actx->base_mrec ? actx->base_mrec : actx->mrec;
|
||||
ni_mrec->link_count = cpu_to_le16(le16_to_cpu(ni_mrec->link_count) - 1);
|
||||
drop_nlink(VFS_I(ni));
|
||||
if (!S_ISDIR(VFS_I(ni)->i_mode))
|
||||
drop_nlink(VFS_I(ni));
|
||||
|
||||
mark_mft_record_dirty(ni);
|
||||
if (looking_for_dos_name) {
|
||||
|
|
@ -955,6 +956,13 @@ static int ntfs_delete(struct ntfs_inode *ni, struct ntfs_inode *dir_ni,
|
|||
goto search;
|
||||
}
|
||||
|
||||
/*
|
||||
* For directories, Drop VFS nlink only when mft record link count
|
||||
* becomes zero. Because we fixes VFS nlink to 1 for directories.
|
||||
*/
|
||||
if (S_ISDIR(VFS_I(ni)->i_mode) && !le16_to_cpu(ni_mrec->link_count))
|
||||
drop_nlink(VFS_I(ni));
|
||||
|
||||
/*
|
||||
* If hard link count is not equal to zero then we are done. In other
|
||||
* case there are no reference to this inode left, so we should free all
|
||||
|
|
@ -1221,7 +1229,8 @@ static int __ntfs_link(struct ntfs_inode *ni, struct ntfs_inode *dir_ni,
|
|||
}
|
||||
/* Increment hard links count. */
|
||||
ni_mrec->link_count = cpu_to_le16(le16_to_cpu(ni_mrec->link_count) + 1);
|
||||
inc_nlink(VFS_I(ni));
|
||||
if (!S_ISDIR(vi->i_mode))
|
||||
inc_nlink(VFS_I(ni));
|
||||
|
||||
/* Done! */
|
||||
mark_mft_record_dirty(ni);
|
||||
|
|
|
|||
|
|
@ -505,7 +505,6 @@ int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni,
|
|||
struct reparse_point *reparse;
|
||||
struct wsl_link_reparse_data *data;
|
||||
|
||||
utarget = (char *)NULL;
|
||||
len = ntfs_ucstonls(ni->vol, target, target_len, &utarget, 0);
|
||||
if (len <= 0)
|
||||
return -EINVAL;
|
||||
|
|
@ -514,7 +513,7 @@ int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni,
|
|||
reparse = kvzalloc(reparse_len, GFP_NOFS);
|
||||
if (!reparse) {
|
||||
err = -ENOMEM;
|
||||
kvfree(utarget);
|
||||
kfree(utarget);
|
||||
} else {
|
||||
data = (struct wsl_link_reparse_data *)reparse->reparse_data;
|
||||
reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
|
||||
|
|
@ -528,6 +527,8 @@ int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni,
|
|||
kvfree(reparse);
|
||||
if (!err)
|
||||
ni->target = utarget;
|
||||
else
|
||||
kfree(utarget);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
* Copyright (c) 2007-2022 Jean-Pierre Andre
|
||||
*/
|
||||
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#include "ntfs.h"
|
||||
#include "attrib.h"
|
||||
|
||||
|
|
@ -739,6 +741,7 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
|
|||
int rlsize; /* Size of runlist buffer. */
|
||||
u16 rlpos; /* Current runlist position in units of struct runlist_elements. */
|
||||
u8 b; /* Current byte offset in buf. */
|
||||
u64 lowest_vcn; /* Raw on-disk lowest_vcn. */
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Make sure attr exists and is non-resident. */
|
||||
|
|
@ -747,8 +750,14 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
lowest_vcn = le64_to_cpu(attr->data.non_resident.lowest_vcn);
|
||||
/* Validate lowest_vcn from on-disk metadata to ensure it is sane. */
|
||||
if (overflows_type(lowest_vcn, vcn)) {
|
||||
ntfs_error(vol->sb, "Invalid lowest_vcn in mapping pairs.");
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
/* Start at vcn = lowest_vcn and lcn 0. */
|
||||
vcn = le64_to_cpu(attr->data.non_resident.lowest_vcn);
|
||||
vcn = lowest_vcn;
|
||||
lcn = 0;
|
||||
/* Get start of the mapping pairs array. */
|
||||
buf = (u8 *)attr +
|
||||
|
|
@ -823,8 +832,17 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
|
|||
* element.
|
||||
*/
|
||||
rl[rlpos].length = deltaxcn;
|
||||
/* Increment the current vcn by the current run length. */
|
||||
vcn += deltaxcn;
|
||||
/*
|
||||
* Increment the current vcn by the current run length.
|
||||
* Guard against s64 overflow from a crafted mapping
|
||||
* pairs array to preserve the monotonically-increasing
|
||||
* vcn invariant.
|
||||
*/
|
||||
if (unlikely(check_add_overflow(vcn, deltaxcn, &vcn))) {
|
||||
ntfs_error(vol->sb, "VCN overflow in mapping pairs array.");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* There might be no lcn change at all, as is the case for
|
||||
* sparse clusters on NTFS 3.0+, in which case we set the lcn
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user