Skip to content

Commit a3033a6

Browse files
gitsterdscho
authored andcommitted
Merge branch 'ps/apply-beyond-symlink' into maint-2.30
Fix a vulnerability (CVE-2023-23946) that allows crafted input to trick `git apply` into writing files outside of the working tree. * ps/apply-beyond-symlink: dir-iterator: prevent top-level symlinks without FOLLOW_SYMLINKS Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 2c9a4c7 + fade728 commit a3033a6

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

apply.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4400,6 +4400,33 @@ static int create_one_file(struct apply_state *state,
44004400
if (state->cached)
44014401
return 0;
44024402

4403+
/*
4404+
* We already try to detect whether files are beyond a symlink in our
4405+
* up-front checks. But in the case where symlinks are created by any
4406+
* of the intermediate hunks it can happen that our up-front checks
4407+
* didn't yet see the symlink, but at the point of arriving here there
4408+
* in fact is one. We thus repeat the check for symlinks here.
4409+
*
4410+
* Note that this does not make the up-front check obsolete as the
4411+
* failure mode is different:
4412+
*
4413+
* - The up-front checks cause us to abort before we have written
4414+
* anything into the working directory. So when we exit this way the
4415+
* working directory remains clean.
4416+
*
4417+
* - The checks here happen in the middle of the action where we have
4418+
* already started to apply the patch. The end result will be a dirty
4419+
* working directory.
4420+
*
4421+
* Ideally, we should update the up-front checks to catch what would
4422+
* happen when we apply the patch before we damage the working tree.
4423+
* We have all the information necessary to do so. But for now, as a
4424+
* part of embargoed security work, having this check would serve as a
4425+
* reasonable first step.
4426+
*/
4427+
if (path_is_beyond_symlink(state, path))
4428+
return error(_("affected file '%s' is beyond a symbolic link"), path);
4429+
44034430
res = try_create_file(state, path, mode, buf, size);
44044431
if (res < 0)
44054432
return -1;

t/t4115-apply-symlink.sh

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,85 @@ test_expect_success 'apply --index symlink patch' '
4444
4545
'
4646

47+
test_expect_success 'symlink setup' '
48+
ln -s .git symlink &&
49+
git add symlink &&
50+
git commit -m "add symlink"
51+
'
52+
53+
test_expect_success SYMLINKS 'symlink escape when creating new files' '
54+
test_when_finished "git reset --hard && git clean -dfx" &&
55+
56+
cat >patch <<-EOF &&
57+
diff --git a/symlink b/renamed-symlink
58+
similarity index 100%
59+
rename from symlink
60+
rename to renamed-symlink
61+
--
62+
diff --git /dev/null b/renamed-symlink/create-me
63+
new file mode 100644
64+
index 0000000..039727e
65+
--- /dev/null
66+
+++ b/renamed-symlink/create-me
67+
@@ -0,0 +1,1 @@
68+
+busted
69+
EOF
70+
71+
test_must_fail git apply patch 2>stderr &&
72+
cat >expected_stderr <<-EOF &&
73+
error: affected file ${SQ}renamed-symlink/create-me${SQ} is beyond a symbolic link
74+
EOF
75+
test_cmp expected_stderr stderr &&
76+
! test_path_exists .git/create-me
77+
'
78+
79+
test_expect_success SYMLINKS 'symlink escape when modifying file' '
80+
test_when_finished "git reset --hard && git clean -dfx" &&
81+
touch .git/modify-me &&
82+
83+
cat >patch <<-EOF &&
84+
diff --git a/symlink b/renamed-symlink
85+
similarity index 100%
86+
rename from symlink
87+
rename to renamed-symlink
88+
--
89+
diff --git a/renamed-symlink/modify-me b/renamed-symlink/modify-me
90+
index 1111111..2222222 100644
91+
--- a/renamed-symlink/modify-me
92+
+++ b/renamed-symlink/modify-me
93+
@@ -0,0 +1,1 @@
94+
+busted
95+
EOF
96+
97+
test_must_fail git apply patch 2>stderr &&
98+
cat >expected_stderr <<-EOF &&
99+
error: renamed-symlink/modify-me: No such file or directory
100+
EOF
101+
test_cmp expected_stderr stderr &&
102+
test_must_be_empty .git/modify-me
103+
'
104+
105+
test_expect_success SYMLINKS 'symlink escape when deleting file' '
106+
test_when_finished "git reset --hard && git clean -dfx && rm .git/delete-me" &&
107+
touch .git/delete-me &&
108+
109+
cat >patch <<-EOF &&
110+
diff --git a/symlink b/renamed-symlink
111+
similarity index 100%
112+
rename from symlink
113+
rename to renamed-symlink
114+
--
115+
diff --git a/renamed-symlink/delete-me b/renamed-symlink/delete-me
116+
deleted file mode 100644
117+
index 1111111..0000000 100644
118+
EOF
119+
120+
test_must_fail git apply patch 2>stderr &&
121+
cat >expected_stderr <<-EOF &&
122+
error: renamed-symlink/delete-me: No such file or directory
123+
EOF
124+
test_cmp expected_stderr stderr &&
125+
test_path_is_file .git/delete-me
126+
'
127+
47128
test_done

0 commit comments

Comments
 (0)