Commit cb6bbff7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents c7bfaff4 736a0516
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);
+93 −0
Original line number Diff line number Diff line
@@ -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;
+46 −11
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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 *);
Loading