[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20250826033557.127367-5-yang.chenzhi@vivo.com>
Date: Tue, 26 Aug 2025 11:35:57 +0800
From: Chenzhi Yang <yang.chenzhi@...o.com>
To: slava@...eyko.com,
glaubitz@...sik.fu-berlin.de,
frank.li@...o.com
Cc: linux-fsdevel@...r.kernel.org,
linux-kernel@...r.kernel.org,
Yang Chenzhi <yang.chenzhi@...o.com>
Subject: [RFC PATCH 4/4] hfs: restructure hfs_brec_lenoff into a returned-value version
From: Yang Chenzhi <yang.chenzhi@...o.com>
Restructure hfs_brec_lenoff into a function that validates
both offset and length. This function now returns an error code
to indicate whether the execution is correct.
This helps fix slab out-of-bounds issues in hfs_bmap_alloc
Replace the old hfs_brec_lenoff interface with the new
version.
Signed-off-by: Yang Chenzhi <yang.chenzhi@...o.com>
---
fs/hfs/bfind.c | 14 +++++++-------
fs/hfs/brec.c | 13 ++++++++++---
fs/hfs/btree.c | 21 +++++++++++++++++----
fs/hfs/btree.h | 2 +-
4 files changed, 35 insertions(+), 15 deletions(-)
diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c
index 34e9804e0f36..aea6edd4d830 100644
--- a/fs/hfs/bfind.c
+++ b/fs/hfs/bfind.c
@@ -61,16 +61,16 @@ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
u16 off, len, keylen;
int rec;
int b, e;
- int res;
+ int res, ret;
b = 0;
e = bnode->num_recs - 1;
res = -ENOENT;
do {
rec = (e + b) / 2;
- len = hfs_brec_lenoff(bnode, rec, &off);
+ ret = hfs_brec_lenoff(bnode, rec, &off, &len);
keylen = hfs_brec_keylen(bnode, rec);
- if (keylen == 0) {
+ if (keylen == 0 || ret) {
res = -EINVAL;
goto fail;
}
@@ -87,9 +87,9 @@ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
e = rec - 1;
} while (b <= e);
if (rec != e && e >= 0) {
- len = hfs_brec_lenoff(bnode, e, &off);
+ ret = hfs_brec_lenoff(bnode, e, &off, &len);
keylen = hfs_brec_keylen(bnode, e);
- if (keylen == 0) {
+ if (keylen == 0 || ret) {
res = -EINVAL;
goto fail;
}
@@ -223,9 +223,9 @@ int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
fd->record += cnt;
}
- len = hfs_brec_lenoff(bnode, fd->record, &off);
+ res = hfs_brec_lenoff(bnode, fd->record, &off, &len);
keylen = hfs_brec_keylen(bnode, fd->record);
- if (keylen == 0) {
+ if (keylen == 0 || res) {
res = -EINVAL;
goto out;
}
diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c
index 896396554bcc..d7026a3ffeea 100644
--- a/fs/hfs/brec.c
+++ b/fs/hfs/brec.c
@@ -16,15 +16,22 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd);
static int hfs_btree_inc_height(struct hfs_btree *tree);
/* Get the length and offset of the given record in the given node */
-u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off)
+int hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off, u16 *len)
{
__be16 retval[2];
u16 dataoff;
+ int res;
dataoff = node->tree->node_size - (rec + 2) * 2;
- hfs_bnode_read(node, retval, dataoff, 4);
+ res = __hfs_bnode_read(node, retval, dataoff, 4);
+ if (res)
+ return -EINVAL;
*off = be16_to_cpu(retval[1]);
- return be16_to_cpu(retval[0]) - *off;
+ *len = be16_to_cpu(retval[0]) - *off;
+ if (!hfs_off_and_len_is_valid(node, *off, *len) ||
+ *off < sizeof(struct hfs_bnode_desc))
+ return -EINVAL;
+ return 0;
}
/* Get the length of the key from a keyed record */
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index e86e1e235658..b13582dcc27a 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -301,7 +301,9 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
node = hfs_bnode_find(tree, nidx);
if (IS_ERR(node))
return node;
- len = hfs_brec_lenoff(node, 2, &off16);
+ res = hfs_brec_lenoff(node, 2, &off16, &len);
+ if (res)
+ return ERR_PTR(res);
off = off16;
off += node->page_offset;
@@ -347,7 +349,9 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
return next_node;
node = next_node;
- len = hfs_brec_lenoff(node, 0, &off16);
+ res = hfs_brec_lenoff(node, 0, &off16, &len);
+ if (res)
+ return ERR_PTR(res);
off = off16;
off += node->page_offset;
pagep = node->page + (off >> PAGE_SHIFT);
@@ -363,6 +367,7 @@ void hfs_bmap_free(struct hfs_bnode *node)
u16 off, len;
u32 nidx;
u8 *data, byte, m;
+ int res;
hfs_dbg(BNODE_MOD, "btree_free_node: %u\n", node->this);
tree = node->tree;
@@ -370,7 +375,9 @@ void hfs_bmap_free(struct hfs_bnode *node)
node = hfs_bnode_find(tree, 0);
if (IS_ERR(node))
return;
- len = hfs_brec_lenoff(node, 2, &off);
+ res = hfs_brec_lenoff(node, 2, &off, &len);
+ if (res)
+ goto fail;
while (nidx >= len * 8) {
u32 i;
@@ -394,7 +401,9 @@ void hfs_bmap_free(struct hfs_bnode *node)
hfs_bnode_put(node);
return;
}
- len = hfs_brec_lenoff(node, 0, &off);
+ res = hfs_brec_lenoff(node, 0, &off, &len);
+ if (res)
+ goto fail;
}
off += node->page_offset + nidx / 8;
page = node->page[off >> PAGE_SHIFT];
@@ -415,4 +424,8 @@ void hfs_bmap_free(struct hfs_bnode *node)
hfs_bnode_put(node);
tree->free_nodes++;
mark_inode_dirty(tree->inode);
+ return;
+fail:
+ hfs_bnode_put(node);
+ pr_err("fail to free a bnode due to invalid off or len\n");
}
diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h
index bf780bf4a016..78f228e62a86 100644
--- a/fs/hfs/btree.h
+++ b/fs/hfs/btree.h
@@ -117,7 +117,7 @@ extern void hfs_bnode_get(struct hfs_bnode *);
extern void hfs_bnode_put(struct hfs_bnode *);
/* brec.c */
-extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
+extern int hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *, u16 *);
extern u16 hfs_brec_keylen(struct hfs_bnode *, u16);
extern int hfs_brec_insert(struct hfs_find_data *, void *, int);
extern int hfs_brec_remove(struct hfs_find_data *);
--
2.43.0
Powered by blists - more mailing lists