From 09ad3b1e99befe042ae5219e4020eb54411d98ef Mon Sep 17 00:00:00 2001 From: Cengiz Can Date: Thu, 6 Feb 2025 17:43:38 +0300 Subject: [PATCH] UBUNTU: SAUCE: fs: hfs/hfsplus: add key_len boundary check to hfs_bnode_read_key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hfs_bnode_read_key is lacking boundary checks on key sizes. This can cause an out-of-bounds (oob) write with an excessively large key_len. Add checks to determine if the computed key_len exceeds the maximum allowed length and return -EINVAL if so. Reported-by: Attila Szász # working with SSD Secure Disclosure CVE-2025-0927 Signed-off-by: Cengiz Can Acked-by: Stefan Bader Acked-by: Manuel Diewald Signed-off-by: Manuel Diewald --- fs/hfs/bnode.c | 8 +++++++- fs/hfs/brec.c | 21 +++++++++++++++++---- fs/hfs/btree.h | 2 +- fs/hfsplus/bnode.c | 8 +++++++- fs/hfsplus/brec.c | 21 +++++++++++++++++---- fs/hfsplus/hfsplus_fs.h | 2 +- 6 files changed, 50 insertions(+), 12 deletions(-) diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 6add6ebfef89..2f00f03e29de 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -55,7 +55,7 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) return data; } -void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) +int hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) { struct hfs_btree *tree; int key_len; @@ -67,7 +67,13 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) else key_len = tree->max_key_len + 1; + if (key_len > tree->max_key_len + 1) { + pr_err("key_len %d too large\n", key_len); + return -EINVAL; + } + hfs_bnode_read(node, key, off, key_len); + return 0; } void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c index 896396554bcc..b3eed02f8679 100644 --- a/fs/hfs/brec.c +++ b/fs/hfs/brec.c @@ -70,6 +70,7 @@ int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) int data_off, end_off; int idx_rec_off, data_rec_off, end_rec_off; __be32 cnid; + int res; tree = fd->tree; if (!fd->bnode) { @@ -138,7 +139,10 @@ skip: * at the start of the node and it is not the new node */ if (!rec && new_node != node) { - hfs_bnode_read_key(node, fd->search_key, data_off + size); + res = hfs_bnode_read_key(node, fd->search_key, data_off + size); + if (res < 0) + return res; + hfs_brec_update_parent(fd); } @@ -156,7 +160,10 @@ skip: entry_len = sizeof(cnid); /* get index key */ - hfs_bnode_read_key(new_node, fd->search_key, 14); + res = hfs_bnode_read_key(new_node, fd->search_key, 14); + if (res < 0) + return res; + __hfs_brec_find(fd->bnode, fd); hfs_bnode_put(new_node); @@ -356,6 +363,7 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd) int newkeylen, diff; int rec, rec_off, end_rec_off; int start_off, end_off; + int res; tree = fd->tree; node = fd->bnode; @@ -431,7 +439,10 @@ skip: } fd->bnode = hfs_bnode_find(tree, new_node->parent); /* create index key and entry */ - hfs_bnode_read_key(new_node, fd->search_key, 14); + res = hfs_bnode_read_key(new_node, fd->search_key, 14); + if (res < 0) + return res; + cnid = cpu_to_be32(new_node->this); __hfs_brec_find(fd->bnode, fd); @@ -443,7 +454,9 @@ skip: if (new_node == node) goto out; /* restore search_key */ - hfs_bnode_read_key(node, fd->search_key, 14); + res = hfs_bnode_read_key(node, fd->search_key, 14); + if (res < 0) + return res; } new_node = NULL; } diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h index 0e6baee93245..dc2cb559efaa 100644 --- a/fs/hfs/btree.h +++ b/fs/hfs/btree.h @@ -97,7 +97,7 @@ extern void hfs_bmap_free(struct hfs_bnode *node); extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int); extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int); extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int); -extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int); +extern int hfs_bnode_read_key(struct hfs_bnode *, void *, int); extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int); extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16); extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8); diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index 87974d5e6791..9d9efa7bc97b 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -54,7 +54,7 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) return data; } -void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) +int hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) { struct hfs_btree *tree; int key_len; @@ -67,7 +67,13 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) else key_len = tree->max_key_len + 2; + if (key_len > tree->max_key_len + 2) { + pr_err("key_len %d too large\n", key_len); + return -EINVAL; + } + hfs_bnode_read(node, key, off, key_len); + return 0; } void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c index 1918544a7871..4db1fd0cf5e9 100644 --- a/fs/hfsplus/brec.c +++ b/fs/hfsplus/brec.c @@ -68,6 +68,7 @@ int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) int data_off, end_off; int idx_rec_off, data_rec_off, end_rec_off; __be32 cnid; + int res; tree = fd->tree; if (!fd->bnode) { @@ -138,7 +139,10 @@ skip: * at the start of the node and it is not the new node */ if (!rec && new_node != node) { - hfs_bnode_read_key(node, fd->search_key, data_off + size); + res = hfs_bnode_read_key(node, fd->search_key, data_off + size); + if (res < 0) + return res; + hfs_brec_update_parent(fd); } @@ -156,7 +160,10 @@ skip: entry_len = sizeof(cnid); /* get index key */ - hfs_bnode_read_key(new_node, fd->search_key, 14); + res = hfs_bnode_read_key(new_node, fd->search_key, 14); + if (res < 0) + return res; + __hfs_brec_find(fd->bnode, fd, hfs_find_rec_by_key); hfs_bnode_put(new_node); @@ -360,6 +367,7 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd) int newkeylen, diff; int rec, rec_off, end_rec_off; int start_off, end_off; + int res; tree = fd->tree; node = fd->bnode; @@ -435,7 +443,10 @@ skip: } fd->bnode = hfs_bnode_find(tree, new_node->parent); /* create index key and entry */ - hfs_bnode_read_key(new_node, fd->search_key, 14); + res = hfs_bnode_read_key(new_node, fd->search_key, 14); + if (res < 0) + return res; + cnid = cpu_to_be32(new_node->this); __hfs_brec_find(fd->bnode, fd, hfs_find_rec_by_key); @@ -447,7 +458,9 @@ skip: if (new_node == node) goto out; /* restore search_key */ - hfs_bnode_read_key(node, fd->search_key, 14); + res = hfs_bnode_read_key(node, fd->search_key, 14); + if (res < 0) + return res; } new_node = NULL; } diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 9e78f181c24f..9f4b5221ead2 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -396,7 +396,7 @@ void hfs_bmap_free(struct hfs_bnode *node); void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len); u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off); u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off); -void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off); +int hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off); void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len); void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data); void hfs_bnode_clear(struct hfs_bnode *node, int off, int len);