]> git.ipfire.org Git - thirdparty/git.git/commitdiff
builtin/clone: abort when hardlinked source and target file differ
authorPatrick Steinhardt <ps@pks.im>
Mon, 15 Apr 2024 11:30:31 +0000 (13:30 +0200)
committerJohannes Schindelin <johannes.schindelin@gmx.de>
Tue, 16 Apr 2024 22:01:25 +0000 (00:01 +0200)
When performing local clones with hardlinks we refuse to copy source
files which are symlinks as a mitigation for CVE-2022-39253. This check
can be raced by an adversary though by changing the file to a symlink
after we have checked it.

Fix the issue by checking whether the hardlinked destination file
matches the source file and abort in case it doesn't.

This addresses CVE-2024-32021.

Reported-by: Apple Product Security <product-security@apple.com>
Suggested-by: Linus Torvalds <torvalds@linuxfoundation.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
builtin/clone.c

index 073e6323d7e188a99c91128d7e62301113d6d9b1..4b80fa0870b6254153ac6b4d813dc22a5324746c 100644 (file)
@@ -357,8 +357,27 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                if (unlink(dest->buf) && errno != ENOENT)
                        die_errno(_("failed to unlink '%s'"), dest->buf);
                if (!option_no_hardlinks) {
-                       if (!link(src->buf, dest->buf))
+                       if (!link(src->buf, dest->buf)) {
+                               struct stat st;
+
+                               /*
+                                * Sanity-check whether the created hardlink
+                                * actually links to the expected file now. This
+                                * catches time-of-check-time-of-use bugs in
+                                * case the source file was meanwhile swapped.
+                                */
+                               if (lstat(dest->buf, &st))
+                                       die(_("hardlink cannot be checked at '%s'"), dest->buf);
+                               if (st.st_mode != iter->st.st_mode ||
+                                   st.st_ino != iter->st.st_ino ||
+                                   st.st_dev != iter->st.st_dev ||
+                                   st.st_size != iter->st.st_size ||
+                                   st.st_uid != iter->st.st_uid ||
+                                   st.st_gid != iter->st.st_gid)
+                                       die(_("hardlink different from source at '%s'"), dest->buf);
+
                                continue;
+                       }
                        if (option_local > 0)
                                die_errno(_("failed to create link '%s'"), dest->buf);
                        option_no_hardlinks = 1;