[PATCH] befs: check NULL return from befs_bt_get_key() and fix keysize in befs_btree_read()
From: Hui Peng
Date: Sat Sep 19 2026 - 18:26:38 EST
Fix two issues in fs/befs/:
1. In befs_btree_find() and befs_btree_read() (fs/befs/btree.c), check
for a NULL return from befs_bt_get_key() when a corrupted B+tree node
has out-of-bounds key offsets or lengths.
2. In befs_btree_read() and befs_readdir() (fs/befs/linuxvfs.c), set
keysize from the actual copied string length rather than the raw
buffer size so uninitialized stack bytes are not passed to filldir().
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@xxxxxxxxx>
---
diff --git a/fs/befs/btree.c b/fs/befs/btree.c
index aa24f1daccdd..67633e5f27fe 100644
--- a/fs/befs/btree.c
+++ b/fs/befs/btree.c
@@ -219,6 +219,19 @@ befs_bt_read_node(struct super_block *sb, const befs_data_stream *ds,
node->head.all_key_length =
fs16_to_cpu(sb, node->od_node->all_key_length);
+ {
+ size_t keylen_off = round_up(sizeof(befs_btree_nodehead) +
+ node->head.all_key_length, 8);
+ size_t total_needed = keylen_off +
+ (size_t)node->head.all_key_count * (sizeof(fs16) + sizeof(fs64));
+ if (off >= node->bh->b_size ||
+ total_needed > node->bh->b_size - off) {
+ brelse(node->bh);
+ node->bh = NULL;
+ return BEFS_ERR;
+ }
+ }
+
befs_debug(sb, "<--- %s", __func__);
return BEFS_OK;
}
@@ -343,6 +356,8 @@ befs_find_key(struct super_block *sb, struct befs_btree_node *node,
/* if node can not contain key, just skip this node */
last = node->head.all_key_count - 1;
thiskey = befs_bt_get_key(sb, node, last, &keylen);
+ if (!thiskey)
+ return BEFS_BT_NOT_FOUND;
eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len);
if (eq < 0) {
@@ -360,6 +375,8 @@ befs_find_key(struct super_block *sb, struct befs_btree_node *node,
befs_debug(sb, "first: %d, last: %d, mid: %d", first, last,
mid);
thiskey = befs_bt_get_key(sb, node, mid, &keylen);
+ if (!thiskey)
+ return BEFS_BT_NOT_FOUND;
eq = befs_compare_strings(thiskey, keylen, findkey,
findkey_len);
@@ -417,6 +434,7 @@ befs_btree_read(struct super_block *sb, const befs_data_stream *ds,
int cur_key;
fs64 *valarray;
char *keystart;
+ ssize_t copy_len;
u16 keylen;
int res;
@@ -487,6 +505,10 @@ befs_btree_read(struct super_block *sb, const befs_data_stream *ds,
valarray = befs_bt_valarray(this_node);
keystart = befs_bt_get_key(sb, this_node, cur_key, &keylen);
+ if (!keystart) {
+ brelse(this_node->bh);
+ goto error_alloc;
+ }
befs_debug(sb, "Read [%llu,%d]: keysize %d",
(long long unsigned int)node_off, (int)cur_key,
@@ -499,9 +521,9 @@ befs_btree_read(struct super_block *sb, const befs_data_stream *ds,
goto error_alloc;
}
- strscpy(keybuf, keystart, keylen + 1);
+ copy_len = strscpy(keybuf, keystart, keylen + 1);
+ *keysize = (copy_len < 0) ? keylen : copy_len;
*value = fs64_to_cpu(sb, valarray[cur_key]);
- *keysize = keylen;
befs_debug(sb, "Read [%llu,%d]: Key \"%.*s\", Value %llu", node_off,
cur_key, keylen, keybuf, *value);
@@ -678,7 +700,7 @@ befs_bt_get_key(struct super_block *sb, struct befs_btree_node *node,
char *keystart;
fs16 *keylen_index;
- if (index < 0 || index > node->head.all_key_count) {
+ if (index < 0 || index >= node->head.all_key_count) {
*keylen = 0;
return NULL;
}
@@ -691,6 +713,12 @@ befs_bt_get_key(struct super_block *sb, struct befs_btree_node *node,
else
prev_key_end = fs16_to_cpu(sb, keylen_index[index - 1]);
+ if (fs16_to_cpu(sb, keylen_index[index]) < prev_key_end ||
+ fs16_to_cpu(sb, keylen_index[index]) > node->head.all_key_length) {
+ *keylen = 0;
+ return NULL;
+ }
+
*keylen = fs16_to_cpu(sb, keylen_index[index]) - prev_key_end;
return keystart + prev_key_end;
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index ee0cbae521b9..de3be4d61c80 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -222,7 +222,7 @@ befs_readdir(struct file *file, struct dir_context *ctx)
befs_off_t value;
int result;
size_t keysize;
- char keybuf[BEFS_NAME_LEN + 1];
+ char keybuf[BEFS_NAME_LEN + 1] = { 0 };
befs_debug(sb, "---> %s name %pD, inode %llu, ctx->pos %lld",
__func__, file, inode->i_ino, ctx->pos);