From 9d35cebb794bb7be93db76c3383979c7deacfef9 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Tue, 17 Oct 2023 14:44:23 -0400
Subject: [PATCH] udf_rename(): only access the child content on
cross-directory rename
Git-commit: 9d35cebb794bb7be93db76c3383979c7deacfef9
Patch-mainline: v6.8-rc1
References: bsc#1221044 CVE-2023-52591
We can't really afford locking the source on same-directory rename;
currently vfs_rename() tries to do that, but it will have to be
changed. The logics in udf_rename() is lazy and goes looking for
".." in source even in same-directory case. It's not hard to get
rid of that, leaving that behaviour only for cross-directory case;
that VFS can get locks safely (and will keep doing that after the
coming changes).
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Jan Kara <jack@suse.cz>
---
fs/udf/namei.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -1084,6 +1084,7 @@ static int udf_rename(struct user_namesp
struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL;
struct fileIdentDesc ocfi, ncfi;
struct buffer_head *dir_bh = NULL;
+ bool is_dir = false;
int retval = -ENOENT;
struct kernel_lb_addr tloc;
struct udf_inode_info *old_iinfo = UDF_I(old_inode);
@@ -1118,13 +1119,16 @@ static int udf_rename(struct user_namesp
nfi = NULL;
}
if (S_ISDIR(old_inode->i_mode)) {
- int offset = udf_ext0_offset(old_inode);
-
if (new_inode) {
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
goto end_rename;
}
+ is_dir = true;
+ }
+ if (is_dir && old_dir != new_dir) {
+ int offset = udf_ext0_offset(old_inode);
+
retval = -EIO;
if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
dir_fi = udf_get_fileident(
@@ -1182,7 +1186,7 @@ static int udf_rename(struct user_namesp
mark_inode_dirty(old_dir);
mark_inode_dirty(new_dir);
- if (dir_fi) {
+ if (is_dir) {
dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location);
udf_update_tag((char *)dir_fi, udf_dir_entry_len(dir_fi));
if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)