]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hfs: fix slab-out-of-bounds in hfs_bnode_read()
authorViacheslav Dubeyko <slava@dubeyko.com>
Thu, 3 Jul 2025 21:49:12 +0000 (14:49 -0700)
committerViacheslav Dubeyko <slava@dubeyko.com>
Fri, 25 Jul 2025 22:37:19 +0000 (15:37 -0700)
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>
fs/hfs/bnode.c

index 50ed4c8553647be4c56432051651782a86491a31..e8cd1a31f2470cfbf72589f08e8c59b5b6c21050 100644 (file)
 
 #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];