mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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:
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user