Commit a431930c authored by Viacheslav Dubeyko's avatar Viacheslav Dubeyko
Browse files

hfs: fix slab-out-of-bounds in hfs_bnode_read()



This patch introduces is_bnode_offset_valid() method that checks
the requested offset value. Also, it introduces
check_and_correct_requested_length() method that checks and
correct the requested length (if it is necessary). These methods
are used in hfs_bnode_read(), hfs_bnode_write(), hfs_bnode_clear(),
hfs_bnode_copy(), and hfs_bnode_move() with the goal to prevent
the access out of allocated memory and triggering the crash.

Signed-off-by: default avatarViacheslav Dubeyko <slava@dubeyko.com>
Link: https://lore.kernel.org/r/20250703214912.244138-1-slava@dubeyko.com


Signed-off-by: default avatarViacheslav Dubeyko <slava@dubeyko.com>
parent c80aa2aa
Loading
Loading
Loading
Loading
+92 −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];