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: Viacheslav Dubeyko <slava@dubeyko.com>
Link: https://lore.kernel.org/r/20250703214912.244138-1-slava@dubeyko.com
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
This commit is contained in:
Viacheslav Dubeyko
2025-07-03 14:49:12 -07:00
parent c80aa2aaaa
commit a431930c9b

View File

@@ -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];