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"
|
#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)
|
void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
|
||||||
{
|
{
|
||||||
struct page *page;
|
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_read;
|
||||||
int bytes_to_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;
|
off += node->page_offset;
|
||||||
pagenum = off >> PAGE_SHIFT;
|
pagenum = off >> PAGE_SHIFT;
|
||||||
off &= ~PAGE_MASK; /* compute page offset for the first page */
|
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;
|
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;
|
off += node->page_offset;
|
||||||
page = node->page[0];
|
page = node->page[0];
|
||||||
|
|
||||||
@@ -104,6 +174,20 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
|
|||||||
{
|
{
|
||||||
struct page *page;
|
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;
|
off += node->page_offset;
|
||||||
page = node->page[0];
|
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);
|
hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
|
||||||
if (!len)
|
if (!len)
|
||||||
return;
|
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;
|
src += src_node->page_offset;
|
||||||
dst += dst_node->page_offset;
|
dst += dst_node->page_offset;
|
||||||
src_page = src_node->page[0];
|
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);
|
hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
|
||||||
if (!len)
|
if (!len)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
len = check_and_correct_requested_length(node, src, len);
|
||||||
|
len = check_and_correct_requested_length(node, dst, len);
|
||||||
|
|
||||||
src += node->page_offset;
|
src += node->page_offset;
|
||||||
dst += node->page_offset;
|
dst += node->page_offset;
|
||||||
page = node->page[0];
|
page = node->page[0];
|
||||||
|
|||||||
Reference in New Issue
Block a user