]> git.ipfire.org Git - thirdparty/git.git/commitdiff
mv: handle lstat() failure correctly
authorSebastian Thiel <sebastian.thiel@icloud.com>
Wed, 9 Aug 2023 07:47:41 +0000 (07:47 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Aug 2023 18:46:12 +0000 (11:46 -0700)
When moving a directory onto another with `git mv` various checks are
performed. One of of these validates that the destination is not existing.

When calling `lstat` on the destination path and it fails as the path
doesn't exist, some environments seem to overwrite the passed  in
`stat` memory nonetheless (I observed this issue on debian 12 of x86_64,
running on OrbStack on ARM, emulated with Rosetta).

This would affect the code that followed as it would still acccess a now
modified `st` structure, which now seems to contain uninitialized memory.
`S_ISDIR(st_dir_mode)` would then typically return false causing the code
to run into a bad case.

The fix avoids overwriting the existing `st` structure, providing an
alternative that exists only for that purpose.

Note that this patch minimizes complexity instead of stack-frame size.

Signed-off-by: Sebastian Thiel <sebastian.thiel@icloud.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/mv.c
t/t7001-mv.sh

index 665bd274485f6c76403e9230539e2650073a47f3..5213d993811134b2d05bb261222e099926b02b59 100644 (file)
@@ -183,7 +183,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        int src_dir_nr = 0, src_dir_alloc = 0;
        struct strbuf a_src_dir = STRBUF_INIT;
        enum update_mode *modes, dst_mode = 0;
-       struct stat st;
+       struct stat st, dest_st;
        struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
        struct lock_file lock_file = LOCK_INIT;
        struct cache_entry *ce;
@@ -303,7 +303,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        goto act_on_entry;
                }
                if (S_ISDIR(st.st_mode)
-                   && lstat(dst, &st) == 0) {
+                   && lstat(dst, &dest_st) == 0) {
                        bad = _("cannot move directory over file");
                        goto act_on_entry;
                }
index 898a92053282d762b10fc2b53b76f84fbd2892e6..f136ea76f7f8a04c7b2b9c5dbf38ca1a50260819 100755 (executable)
@@ -174,6 +174,13 @@ test_expect_success 'do not move directory over existing directory' '
        test_must_fail git mv path2 path0
 '
 
+test_expect_success 'rename directory to non-existing directory' '
+       mkdir dir-a &&
+       >dir-a/f &&
+       git add dir-a &&
+       git mv dir-a non-existing-dir
+'
+
 test_expect_success 'move into "."' '
        git mv path1/path2/ .
 '