[PATCH] udf: Fix slab-out-of-bound write in udf_write_aext

From: Abdun Nihaal
Date: Thu Dec 29 2022 - 00:02:28 EST


Syzbot reports an out of bound write in udf_write_aext.
This bug can be reproduced by mounting a UDF filesystem with the
noadinicb mount option and then creating a directory inside.

The bug is caused because udf_add_entry function tries to incorrectly
round off the previous extent for the newly created inode.

Fix the bug by creating an extent before calling udf_add_entry when the
noadinicb option is used, in udf_mkdir function.

Link: https://syzkaller.appspot.com/bug?id=5870f4bbc7294f2b4405a408e35e7e96100fb284
Reported-and-tested-by: syzbot+908340a8367281960537@xxxxxxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Abdun Nihaal <abdun.nihaal@xxxxxxxxx>
---
fs/udf/namei.c | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)

diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 7c95c549dd64..796fd3be3769 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -678,6 +678,47 @@ static int udf_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
iinfo = UDF_I(inode);
inode->i_op = &udf_dir_inode_operations;
inode->i_fop = &udf_dir_operations;
+ if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+ struct kernel_lb_addr eloc;
+ struct extent_position epos = {};
+ udf_pblk_t block;
+ uint32_t bsize;
+
+ block = udf_new_block(dir->i_sb, inode,
+ iinfo->i_location.partitionReferenceNum,
+ iinfo->i_location.logicalBlockNum, &err);
+ if (!block) {
+ inode_dec_link_count(inode);
+ discard_new_inode(inode);
+ goto out;
+ }
+ epos.block = iinfo->i_location;
+ epos.offset = udf_file_entry_alloc_offset(inode);
+ epos.bh = NULL;
+ eloc.logicalBlockNum = block;
+ eloc.partitionReferenceNum =
+ iinfo->i_location.partitionReferenceNum;
+ bsize = dir->i_sb->s_blocksize;
+ iinfo->i_lenExtents = bsize;
+ udf_add_aext(inode, &epos, &eloc, bsize, 0);
+ brelse(epos.bh);
+
+ block = udf_get_pblock(dir->i_sb, block,
+ iinfo->i_location.partitionReferenceNum,
+ 0);
+ epos.bh = udf_tgetblk(dir->i_sb, block);
+ if (unlikely(!epos.bh)) {
+ err = -ENOMEM;
+ inode_dec_link_count(inode);
+ discard_new_inode(inode);
+ goto out;
+ }
+ lock_buffer(epos.bh);
+ memset(epos.bh->b_data, 0x00, bsize);
+ set_buffer_uptodate(epos.bh);
+ unlock_buffer(epos.bh);
+ mark_buffer_dirty_inode(epos.bh, inode);
+ }
fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err);
if (!fi) {
inode_dec_link_count(inode);
--
2.38.1