mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
hfs/hfsplus updates for v6.17
- hfs: fix general protection fault in hfs_find_init() - hfs: fix slab-out-of-bounds in hfs_bnode_read() - hfsplus: fix slab-out-of-bounds in hfsplus_bnode_read() - hfsplus: fix slab-out-of-bounds read in hfsplus_uni2asc() - hfsplus: don't use BUG_ON() in hfsplus_create_attributes_file() - hfsplus: don't set REQ_SYNC for hfsplus_submit_bio() - hfsplus: remove mutex_lock check in hfsplus_free_extents - hfs: make splice write available again - hfsplus: make splice write available again - hfs: fix not erasing deleted b-tree node issue -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQT4wVoLCG92poNnMFAhI4xTh21NnQUCaIQQ0wAKCRAhI4xTh21N nW3yAQDMhJcNyjP1j2dhNRq8l2PO6jDJqLhxAYGKwWMwv1GTvQD5AaOUSeMQbmcs hNkMtjzb7OlfBLUthvrWlaCfLKWCmAk= =dI94 -----END PGP SIGNATURE----- Merge tag 'hfs-v6.17-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs Pull hfs/hfsplus updates from Viacheslav Dubeyko: "Johannes Thumshirn has made nice cleanup in hfsplus_submit_bio(). Tetsuo Handa has fixed the syzbot reported issue in hfsplus_create_attributes_file() for the case of corruption the Attributes File's metadata. Yangtao Li has fixed the syzbot reported issue by removing the uneccessary WARN_ON() in hfsplus_free_extents(). Other fixes: - restore generic/001 successful execution by erasing deleted b-tree nodes - eliminate slab-out-of-bounds issue in hfs_bnode_read() and hfsplus_bnode_read() by checking correctness of offset and length when accessing b-tree node contents - eliminate slab-out-of-bounds read in hfsplus_uni2asc() if the b-tree node record has corrupted length of a name that could be bigger than HFSPLUS_MAX_STRLEN - eliminate general protection fault in hfs_find_init() for the case of initial b-tree object creation" * tag 'hfs-v6.17-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs: hfs: fix general protection fault in hfs_find_init() hfs: fix slab-out-of-bounds in hfs_bnode_read() hfsplus: fix slab-out-of-bounds in hfsplus_bnode_read() hfsplus: fix slab-out-of-bounds read in hfsplus_uni2asc() hfsplus: don't use BUG_ON() in hfsplus_create_attributes_file() hfsplus: don't set REQ_SYNC for hfsplus_submit_bio() hfsplus: remove mutex_lock check in hfsplus_free_extents hfs: make splice write available again hfsplus: make splice write available again hfs: fix not erasing deleted b-tree node issue
This commit is contained in:
commit
cb6bbff7e6
12 changed files with 252 additions and 20 deletions
|
@ -16,6 +16,9 @@ int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd)
|
|||
{
|
||||
void *ptr;
|
||||
|
||||
if (!tree || !fd)
|
||||
return -EINVAL;
|
||||
|
||||
fd->tree = tree;
|
||||
fd->bnode = NULL;
|
||||
ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL);
|
||||
|
|
|
@ -15,6 +15,48 @@
|
|||
|
||||
#include "btree.h"
|
||||
|
||||
static inline
|
||||
bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
|
||||
{
|
||||
bool is_valid = off < node->tree->node_size;
|
||||
|
||||
if (!is_valid) {
|
||||
pr_err("requested invalid offset: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off);
|
||||
}
|
||||
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
static inline
|
||||
int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
|
||||
{
|
||||
unsigned int node_size;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return 0;
|
||||
|
||||
node_size = node->tree->node_size;
|
||||
|
||||
if ((off + len) > node_size) {
|
||||
int new_len = (int)node_size - off;
|
||||
|
||||
pr_err("requested length has been corrected: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, "
|
||||
"requested_len %d, corrected_len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len, new_len);
|
||||
|
||||
return new_len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
|
||||
{
|
||||
struct page *page;
|
||||
|
@ -22,6 +64,20 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
|
|||
int bytes_read;
|
||||
int bytes_to_read;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
pr_err("requested zero length: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
len = check_and_correct_requested_length(node, off, len);
|
||||
|
||||
off += node->page_offset;
|
||||
pagenum = off >> PAGE_SHIFT;
|
||||
off &= ~PAGE_MASK; /* compute page offset for the first page */
|
||||
|
@ -80,6 +136,20 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
|
|||
{
|
||||
struct page *page;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
pr_err("requested zero length: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
len = check_and_correct_requested_length(node, off, len);
|
||||
|
||||
off += node->page_offset;
|
||||
page = node->page[0];
|
||||
|
||||
|
@ -104,6 +174,20 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
|
|||
{
|
||||
struct page *page;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
pr_err("requested zero length: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
len = check_and_correct_requested_length(node, off, len);
|
||||
|
||||
off += node->page_offset;
|
||||
page = node->page[0];
|
||||
|
||||
|
@ -119,6 +203,10 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
|
|||
hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
len = check_and_correct_requested_length(src_node, src, len);
|
||||
len = check_and_correct_requested_length(dst_node, dst, len);
|
||||
|
||||
src += src_node->page_offset;
|
||||
dst += dst_node->page_offset;
|
||||
src_page = src_node->page[0];
|
||||
|
@ -136,6 +224,10 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
|
|||
hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
len = check_and_correct_requested_length(node, src, len);
|
||||
len = check_and_correct_requested_length(node, dst, len);
|
||||
|
||||
src += node->page_offset;
|
||||
dst += node->page_offset;
|
||||
page = node->page[0];
|
||||
|
@ -482,6 +574,7 @@ void hfs_bnode_put(struct hfs_bnode *node)
|
|||
if (test_bit(HFS_BNODE_DELETED, &node->flags)) {
|
||||
hfs_bnode_unhash(node);
|
||||
spin_unlock(&tree->hash_lock);
|
||||
hfs_bnode_clear(node, 0, tree->node_size);
|
||||
hfs_bmap_free(node);
|
||||
hfs_bnode_free(node);
|
||||
return;
|
||||
|
|
|
@ -21,8 +21,12 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke
|
|||
struct hfs_btree *tree;
|
||||
struct hfs_btree_header_rec *head;
|
||||
struct address_space *mapping;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
struct buffer_head *bh;
|
||||
unsigned int size;
|
||||
u16 dblock;
|
||||
sector_t start_block;
|
||||
loff_t offset;
|
||||
|
||||
tree = kzalloc(sizeof(*tree), GFP_KERNEL);
|
||||
if (!tree)
|
||||
|
@ -75,12 +79,40 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke
|
|||
unlock_new_inode(tree->inode);
|
||||
|
||||
mapping = tree->inode->i_mapping;
|
||||
page = read_mapping_page(mapping, 0, NULL);
|
||||
if (IS_ERR(page))
|
||||
folio = filemap_grab_folio(mapping, 0);
|
||||
if (IS_ERR(folio))
|
||||
goto free_inode;
|
||||
|
||||
folio_zero_range(folio, 0, folio_size(folio));
|
||||
|
||||
dblock = hfs_ext_find_block(HFS_I(tree->inode)->first_extents, 0);
|
||||
start_block = HFS_SB(sb)->fs_start + (dblock * HFS_SB(sb)->fs_div);
|
||||
|
||||
size = folio_size(folio);
|
||||
offset = 0;
|
||||
while (size > 0) {
|
||||
size_t len;
|
||||
|
||||
bh = sb_bread(sb, start_block);
|
||||
if (!bh) {
|
||||
pr_err("unable to read tree header\n");
|
||||
goto put_folio;
|
||||
}
|
||||
|
||||
len = min_t(size_t, folio_size(folio), sb->s_blocksize);
|
||||
memcpy_to_folio(folio, offset, bh->b_data, sb->s_blocksize);
|
||||
|
||||
brelse(bh);
|
||||
|
||||
start_block++;
|
||||
offset += len;
|
||||
size -= len;
|
||||
}
|
||||
|
||||
folio_mark_uptodate(folio);
|
||||
|
||||
/* Load the header */
|
||||
head = (struct hfs_btree_header_rec *)(kmap_local_page(page) +
|
||||
head = (struct hfs_btree_header_rec *)(kmap_local_folio(folio, 0) +
|
||||
sizeof(struct hfs_bnode_desc));
|
||||
tree->root = be32_to_cpu(head->root);
|
||||
tree->leaf_count = be32_to_cpu(head->leaf_count);
|
||||
|
@ -95,22 +127,22 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke
|
|||
|
||||
size = tree->node_size;
|
||||
if (!is_power_of_2(size))
|
||||
goto fail_page;
|
||||
goto fail_folio;
|
||||
if (!tree->node_count)
|
||||
goto fail_page;
|
||||
goto fail_folio;
|
||||
switch (id) {
|
||||
case HFS_EXT_CNID:
|
||||
if (tree->max_key_len != HFS_MAX_EXT_KEYLEN) {
|
||||
pr_err("invalid extent max_key_len %d\n",
|
||||
tree->max_key_len);
|
||||
goto fail_page;
|
||||
goto fail_folio;
|
||||
}
|
||||
break;
|
||||
case HFS_CAT_CNID:
|
||||
if (tree->max_key_len != HFS_MAX_CAT_KEYLEN) {
|
||||
pr_err("invalid catalog max_key_len %d\n",
|
||||
tree->max_key_len);
|
||||
goto fail_page;
|
||||
goto fail_folio;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -121,12 +153,15 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke
|
|||
tree->pages_per_bnode = (tree->node_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
|
||||
kunmap_local(head);
|
||||
put_page(page);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
return tree;
|
||||
|
||||
fail_page:
|
||||
fail_folio:
|
||||
kunmap_local(head);
|
||||
put_page(page);
|
||||
put_folio:
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
free_inode:
|
||||
tree->inode->i_mapping->a_ops = &hfs_aops;
|
||||
iput(tree->inode);
|
||||
|
|
|
@ -71,7 +71,7 @@ int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2)
|
|||
*
|
||||
* Find a block within an extent record
|
||||
*/
|
||||
static u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off)
|
||||
u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off)
|
||||
{
|
||||
int i;
|
||||
u16 count;
|
||||
|
|
|
@ -190,6 +190,7 @@ extern const struct inode_operations hfs_dir_inode_operations;
|
|||
|
||||
/* extent.c */
|
||||
extern int hfs_ext_keycmp(const btree_key *, const btree_key *);
|
||||
extern u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off);
|
||||
extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int);
|
||||
extern int hfs_ext_write_extent(struct inode *);
|
||||
extern int hfs_extend_file(struct inode *);
|
||||
|
|
|
@ -692,6 +692,7 @@ static const struct file_operations hfs_file_operations = {
|
|||
.write_iter = generic_file_write_iter,
|
||||
.mmap_prepare = generic_file_mmap_prepare,
|
||||
.splice_read = filemap_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
.fsync = hfs_file_fsync,
|
||||
.open = hfs_file_open,
|
||||
.release = hfs_file_release,
|
||||
|
|
|
@ -18,12 +18,68 @@
|
|||
#include "hfsplus_fs.h"
|
||||
#include "hfsplus_raw.h"
|
||||
|
||||
static inline
|
||||
bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
|
||||
{
|
||||
bool is_valid = off < node->tree->node_size;
|
||||
|
||||
if (!is_valid) {
|
||||
pr_err("requested invalid offset: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off);
|
||||
}
|
||||
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
static inline
|
||||
int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
|
||||
{
|
||||
unsigned int node_size;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return 0;
|
||||
|
||||
node_size = node->tree->node_size;
|
||||
|
||||
if ((off + len) > node_size) {
|
||||
int new_len = (int)node_size - off;
|
||||
|
||||
pr_err("requested length has been corrected: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, "
|
||||
"requested_len %d, corrected_len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len, new_len);
|
||||
|
||||
return new_len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Copy a specified range of bytes from the raw data of a node */
|
||||
void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
|
||||
{
|
||||
struct page **pagep;
|
||||
int l;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
pr_err("requested zero length: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
len = check_and_correct_requested_length(node, off, len);
|
||||
|
||||
off += node->page_offset;
|
||||
pagep = node->page + (off >> PAGE_SHIFT);
|
||||
off &= ~PAGE_MASK;
|
||||
|
@ -81,6 +137,20 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
|
|||
struct page **pagep;
|
||||
int l;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
pr_err("requested zero length: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
len = check_and_correct_requested_length(node, off, len);
|
||||
|
||||
off += node->page_offset;
|
||||
pagep = node->page + (off >> PAGE_SHIFT);
|
||||
off &= ~PAGE_MASK;
|
||||
|
@ -109,6 +179,20 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
|
|||
struct page **pagep;
|
||||
int l;
|
||||
|
||||
if (!is_bnode_offset_valid(node, off))
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
pr_err("requested zero length: "
|
||||
"NODE: id %u, type %#x, height %u, "
|
||||
"node_size %u, offset %d, len %d\n",
|
||||
node->this, node->type, node->height,
|
||||
node->tree->node_size, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
len = check_and_correct_requested_length(node, off, len);
|
||||
|
||||
off += node->page_offset;
|
||||
pagep = node->page + (off >> PAGE_SHIFT);
|
||||
off &= ~PAGE_MASK;
|
||||
|
@ -133,6 +217,10 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
|
|||
hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
len = check_and_correct_requested_length(src_node, src, len);
|
||||
len = check_and_correct_requested_length(dst_node, dst, len);
|
||||
|
||||
src += src_node->page_offset;
|
||||
dst += dst_node->page_offset;
|
||||
src_page = src_node->page + (src >> PAGE_SHIFT);
|
||||
|
@ -187,6 +275,10 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
|
|||
hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
len = check_and_correct_requested_length(node, src, len);
|
||||
len = check_and_correct_requested_length(node, dst, len);
|
||||
|
||||
src += node->page_offset;
|
||||
dst += node->page_offset;
|
||||
if (dst > src) {
|
||||
|
|
|
@ -342,9 +342,6 @@ static int hfsplus_free_extents(struct super_block *sb,
|
|||
int i;
|
||||
int err = 0;
|
||||
|
||||
/* Mapping the allocation file may lock the extent tree */
|
||||
WARN_ON(mutex_is_locked(&HFSPLUS_SB(sb)->ext_tree->tree_lock));
|
||||
|
||||
hfsplus_dump_extent(extent);
|
||||
for (i = 0; i < 8; extent++, i++) {
|
||||
count = be32_to_cpu(extent->block_count);
|
||||
|
|
|
@ -370,6 +370,7 @@ static const struct file_operations hfsplus_file_operations = {
|
|||
.write_iter = generic_file_write_iter,
|
||||
.mmap_prepare = generic_file_mmap_prepare,
|
||||
.splice_read = filemap_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
.fsync = hfsplus_file_fsync,
|
||||
.open = hfsplus_file_open,
|
||||
.release = hfsplus_file_release,
|
||||
|
|
|
@ -222,8 +222,7 @@ static int hfsplus_sync_fs(struct super_block *sb, int wait)
|
|||
|
||||
error2 = hfsplus_submit_bio(sb,
|
||||
sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
|
||||
sbi->s_vhdr_buf, NULL, REQ_OP_WRITE |
|
||||
REQ_SYNC);
|
||||
sbi->s_vhdr_buf, NULL, REQ_OP_WRITE);
|
||||
if (!error)
|
||||
error = error2;
|
||||
if (!write_backup)
|
||||
|
@ -231,8 +230,7 @@ static int hfsplus_sync_fs(struct super_block *sb, int wait)
|
|||
|
||||
error2 = hfsplus_submit_bio(sb,
|
||||
sbi->part_start + sbi->sect_count - 2,
|
||||
sbi->s_backup_vhdr_buf, NULL, REQ_OP_WRITE |
|
||||
REQ_SYNC);
|
||||
sbi->s_backup_vhdr_buf, NULL, REQ_OP_WRITE);
|
||||
if (!error)
|
||||
error2 = error;
|
||||
out:
|
||||
|
|
|
@ -132,7 +132,14 @@ int hfsplus_uni2asc(struct super_block *sb,
|
|||
|
||||
op = astr;
|
||||
ip = ustr->unicode;
|
||||
|
||||
ustrlen = be16_to_cpu(ustr->length);
|
||||
if (ustrlen > HFSPLUS_MAX_STRLEN) {
|
||||
ustrlen = HFSPLUS_MAX_STRLEN;
|
||||
pr_err("invalid length %u has been corrected to %d\n",
|
||||
be16_to_cpu(ustr->length), ustrlen);
|
||||
}
|
||||
|
||||
len = *len_p;
|
||||
ce1 = NULL;
|
||||
compose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
|
|
|
@ -172,7 +172,11 @@ check_attr_tree_state_again:
|
|||
return PTR_ERR(attr_file);
|
||||
}
|
||||
|
||||
BUG_ON(i_size_read(attr_file) != 0);
|
||||
if (i_size_read(attr_file) != 0) {
|
||||
err = -EIO;
|
||||
pr_err("detected inconsistent attributes file, running fsck.hfsplus is recommended.\n");
|
||||
goto end_attr_file_creation;
|
||||
}
|
||||
|
||||
hip = HFSPLUS_I(attr_file);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue