Blob Blame History Raw
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)