summaryrefslogtreecommitdiffstats
path: root/meta
diff options
context:
space:
mode:
authorMinjae Kim <flowergom@gmail.com>2021-03-27 14:15:38 +0900
committerRichard Purdie <richard.purdie@linuxfoundation.org>2021-04-19 12:55:20 +0100
commitea4682c61d7a463d82bab57da580573f91ce9000 (patch)
tree14002c88715ef7e11c4ba2ce75229de416395efc /meta
parentf1b820e368122ae64d78099fbe50b7a267ad9a67 (diff)
downloadpoky-ea4682c61d7a463d82bab57da580573f91ce9000.tar.gz
git: fix CVE-2021-21300
checkout: fix bug that makes checkout follow symlinks in leading path Upstream-Status: Acepted [https://github.com/git/git/commit/684dd4c2b414bcf648505e74498a608f28de4592] CVE: CVE-2021-21300 (From OE-Core rev: 51450bb2a1cce636258c207d52696e436daa5097) Signed-off-by: Minjae Kim <flowergom@gmail.com> Signed-off-by: Anuj Mittal <anuj.mittal@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
-rw-r--r--meta/recipes-devtools/git/git.inc4
-rw-r--r--meta/recipes-devtools/git/git/CVE-2021-21300.patch304
2 files changed, 307 insertions, 1 deletions
diff --git a/meta/recipes-devtools/git/git.inc b/meta/recipes-devtools/git/git.inc
index 586a305b27..3e78254eec 100644
--- a/meta/recipes-devtools/git/git.inc
+++ b/meta/recipes-devtools/git/git.inc
@@ -8,7 +8,9 @@ PROVIDES_append_class-native = " git-replacement-native"
8 8
9SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \ 9SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \
10 ${KERNELORG_MIRROR}/software/scm/git/git-manpages-${PV}.tar.gz;name=manpages \ 10 ${KERNELORG_MIRROR}/software/scm/git/git-manpages-${PV}.tar.gz;name=manpages \
11 file://fixsort.patch" 11 file://fixsort.patch \
12 file://CVE-2021-21300.patch \
13"
12 14
13S = "${WORKDIR}/git-${PV}" 15S = "${WORKDIR}/git-${PV}"
14 16
diff --git a/meta/recipes-devtools/git/git/CVE-2021-21300.patch b/meta/recipes-devtools/git/git/CVE-2021-21300.patch
new file mode 100644
index 0000000000..390570fe78
--- /dev/null
+++ b/meta/recipes-devtools/git/git/CVE-2021-21300.patch
@@ -0,0 +1,304 @@
1From ba07d31bd2140190c4d8c197c9b8a503544b4c29 Mon Sep 17 00:00:00 2001
2From: Minjae Kim <flowrgom@gmail.com>
3Date: Sat, 27 Mar 2021 14:05:56 +0900
4Subject: [PATCH] checkout: fix bug that makes checkout follow symlinks in
5 leading path
6
7Before checking out a file, we have to confirm that all of its leading
8components are real existing directories. And to reduce the number of
9lstat() calls in this process, we cache the last leading path known to
10contain only directories. However, when a path collision occurs (e.g.
11when checking out case-sensitive files in case-insensitive file
12systems), a cached path might have its file type changed on disk,
13leaving the cache on an invalid state. Normally, this doesn't bring
14any bad consequences as we usually check out files in index order, and
15therefore, by the time the cached path becomes outdated, we no longer
16need it anyway (because all files in that directory would have already
17been written).
18
19But, there are some users of the checkout machinery that do not always
20follow the index order. In particular: checkout-index writes the paths
21in the same order that they appear on the CLI (or stdin); and the
22delayed checkout feature -- used when a long-running filter process
23replies with "status=delayed" -- postpones the checkout of some entries,
24thus modifying the checkout order.
25
26When we have to check out an out-of-order entry and the lstat() cache is
27invalid (due to a previous path collision), checkout_entry() may end up
28using the invalid data and thrusting that the leading components are
29real directories when, in reality, they are not. In the best case
30scenario, where the directory was replaced by a regular file, the user
31will get an error: "fatal: unable to create file 'foo/bar': Not a
32directory". But if the directory was replaced by a symlink, checkout
33could actually end up following the symlink and writing the file at a
34wrong place, even outside the repository. Since delayed checkout is
35affected by this bug, it could be used by an attacker to write
36arbitrary files during the clone of a maliciously crafted repository.
37
38Some candidate solutions considered were to disable the lstat() cache
39during unordered checkouts or sort the entries before passing them to
40the checkout machinery. But both ideas include some performance penalty
41and they don't future-proof the code against new unordered use cases.
42
43Instead, we now manually reset the lstat cache whenever we successfully
44remove a directory. Note: We are not even checking whether the directory
45was the same as the lstat cache points to because we might face a
46scenario where the paths refer to the same location but differ due to
47case folding, precomposed UTF-8 issues, or the presence of `..`
48components in the path. Two regression tests, with case-collisions and
49utf8-collisions, are also added for both checkout-index and delayed
50checkout.
51
52Note: to make the previously mentioned clone attack unfeasible, it would
53be sufficient to reset the lstat cache only after the remove_subtree()
54call inside checkout_entry(). This is the place where we would remove a
55directory whose path collides with the path of another entry that we are
56currently trying to check out (possibly a symlink). However, in the
57interest of a thorough fix that does not leave Git open to
58similar-but-not-identical attack vectors, we decided to intercept
59all `rmdir()` calls in one fell swoop.
60
61This addresses CVE-2021-21300.
62
63Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
64Signed-off-by: Matheus Tavares <matheus.bernardino@usp.br>
65
66Upstream-Status: Acepted [https://github.com/git/git/commit/684dd4c2b414bcf648505e74498a608f28de4592]
67CVE: CVE-2021-21300
68Signed-off-by: Minjae Kim <flowergom@gmail.com>
69---
70 cache.h | 1 +
71 compat/mingw.c | 2 ++
72 git-compat-util.h | 5 +++++
73 symlinks.c | 24 ++++++++++++++++++++
74 t/t0021-conversion.sh | 39 ++++++++++++++++++++++++++++++++
75 t/t0021/rot13-filter.pl | 21 ++++++++++++++---
76 t/t2006-checkout-index-basic.sh | 40 +++++++++++++++++++++++++++++++++
77 7 files changed, 129 insertions(+), 3 deletions(-)
78
79diff --git a/cache.h b/cache.h
80index 6544264..64226c3 100644
81--- a/cache.h
82+++ b/cache.h
83@@ -1733,6 +1733,7 @@ int has_symlink_leading_path(const char *name, int len);
84 int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
85 int check_leading_path(const char *name, int len);
86 int has_dirs_only_path(const char *name, int len, int prefix_len);
87+extern void invalidate_lstat_cache(void);
88 void schedule_dir_for_removal(const char *name, int len);
89 void remove_scheduled_dirs(void);
90
91diff --git a/compat/mingw.c b/compat/mingw.c
92index 8ee0b64..be2b88e 100644
93--- a/compat/mingw.c
94+++ b/compat/mingw.c
95@@ -364,6 +364,8 @@ int mingw_rmdir(const char *pathname)
96 ask_yes_no_if_possible("Deletion of directory '%s' failed. "
97 "Should I try again?", pathname))
98 ret = _wrmdir(wpathname);
99+ if (!ret)
100+ invalidate_lstat_cache();
101 return ret;
102 }
103
104diff --git a/git-compat-util.h b/git-compat-util.h
105index 5637114..d983853 100644
106--- a/git-compat-util.h
107+++ b/git-compat-util.h
108@@ -345,6 +345,11 @@ static inline int noop_core_config(const char *var, const char *value, void *cb)
109 #define platform_core_config noop_core_config
110 #endif
111
112+int lstat_cache_aware_rmdir(const char *path);
113+#if !defined(__MINGW32__) && !defined(_MSC_VER)
114+#define rmdir lstat_cache_aware_rmdir
115+#endif
116+
117 #ifndef has_dos_drive_prefix
118 static inline int git_has_dos_drive_prefix(const char *path)
119 {
120diff --git a/symlinks.c b/symlinks.c
121index 69d458a..7dbb6b2 100644
122--- a/symlinks.c
123+++ b/symlinks.c
124@@ -267,6 +267,13 @@ int has_dirs_only_path(const char *name, int len, int prefix_len)
125 */
126 static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len)
127 {
128+ /*
129+ * Note: this function is used by the checkout machinery, which also
130+ * takes care to properly reset the cache when it performs an operation
131+ * that would leave the cache outdated. If this function starts caching
132+ * anything else besides FL_DIR, remember to also invalidate the cache
133+ * when creating or deleting paths that might be in the cache.
134+ */
135 return lstat_cache(cache, name, len,
136 FL_DIR|FL_FULLPATH, prefix_len) &
137 FL_DIR;
138@@ -321,3 +328,20 @@ void remove_scheduled_dirs(void)
139 {
140 do_remove_scheduled_dirs(0);
141 }
142+
143+void invalidate_lstat_cache(void)
144+{
145+ reset_lstat_cache(&default_cache);
146+}
147+
148+#undef rmdir
149+int lstat_cache_aware_rmdir(const char *path)
150+{
151+ /* Any change in this function must be made also in `mingw_rmdir()` */
152+ int ret = rmdir(path);
153+
154+ if (!ret)
155+ invalidate_lstat_cache();
156+
157+ return ret;
158+}
159diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
160index 4bfffa9..c42f51e 100755
161--- a/t/t0021-conversion.sh
162+++ b/t/t0021-conversion.sh
163@@ -957,4 +957,43 @@ test_expect_success PERL 'invalid file in delayed checkout' '
164 grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log
165 '
166
167+for mode in 'case' 'utf-8'
168+do
169+ case "$mode" in
170+ case) dir='A' symlink='a' mode_prereq='CASE_INSENSITIVE_FS' ;;
171+ utf-8)
172+ dir=$(printf "\141\314\210") symlink=$(printf "\303\244")
173+ mode_prereq='UTF8_NFD_TO_NFC' ;;
174+ esac
175+
176+ test_expect_success PERL,SYMLINKS,$mode_prereq \
177+ "delayed checkout with $mode-collision don't write to the wrong place" '
178+ test_config_global filter.delay.process \
179+ "\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
180+ test_config_global filter.delay.required true &&
181+ git init $mode-collision &&
182+ (
183+ cd $mode-collision &&
184+ mkdir target-dir &&
185+ empty_oid=$(printf "" | git hash-object -w --stdin) &&
186+ symlink_oid=$(printf "%s" "$PWD/target-dir" | git hash-object -w --stdin) &&
187+ attr_oid=$(echo "$dir/z filter=delay" | git hash-object -w --stdin) &&
188+ cat >objs <<-EOF &&
189+ 100644 blob $empty_oid $dir/x
190+ 100644 blob $empty_oid $dir/y
191+ 100644 blob $empty_oid $dir/z
192+ 120000 blob $symlink_oid $symlink
193+ 100644 blob $attr_oid .gitattributes
194+ EOF
195+ git update-index --index-info <objs &&
196+ git commit -m "test commit"
197+ ) &&
198+ git clone $mode-collision $mode-collision-cloned &&
199+ # Make sure z was really delayed
200+ grep "IN: smudge $dir/z .* \\[DELAYED\\]" $mode-collision-cloned/delayed.log &&
201+ # Should not create $dir/z at $symlink/z
202+ test_path_is_missing $mode-collision/target-dir/z
203+ '
204+done
205+
206 test_done
207diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
208index cd32a82..7bb9376 100644
209--- a/t/t0021/rot13-filter.pl
210+++ b/t/t0021/rot13-filter.pl
211@@ -2,9 +2,15 @@
212 # Example implementation for the Git filter protocol version 2
213 # See Documentation/gitattributes.txt, section "Filter Protocol"
214 #
215-# The first argument defines a debug log file that the script write to.
216-# All remaining arguments define a list of supported protocol
217-# capabilities ("clean", "smudge", etc).
218+# Usage: rot13-filter.pl [--always-delay] <log path> <capabilities>
219+#
220+# Log path defines a debug log file that the script writes to. The
221+# subsequent arguments define a list of supported protocol capabilities
222+# ("clean", "smudge", etc).
223+#
224+# When --always-delay is given all pathnames with the "can-delay" flag
225+# that don't appear on the list bellow are delayed with a count of 1
226+# (see more below).
227 #
228 # This implementation supports special test cases:
229 # (1) If data with the pathname "clean-write-fail.r" is processed with
230@@ -53,6 +59,13 @@ sub gitperllib {
231 use Git::Packet;
232
233 my $MAX_PACKET_CONTENT_SIZE = 65516;
234+
235+my $always_delay = 0;
236+if ( $ARGV[0] eq '--always-delay' ) {
237+ $always_delay = 1;
238+ shift @ARGV;
239+}
240+
241 my $log_file = shift @ARGV;
242 my @capabilities = @ARGV;
243
244@@ -134,6 +147,8 @@ sub rot13 {
245 if ( $buffer eq "can-delay=1" ) {
246 if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
247 $DELAY{$pathname}{"requested"} = 1;
248+ } elsif ( !exists $DELAY{$pathname} and $always_delay ) {
249+ $DELAY{$pathname} = { "requested" => 1, "count" => 1 };
250 }
251 } elsif ($buffer =~ /^(ref|treeish|blob)=/) {
252 print $debug " $buffer";
253diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
254index 57cbdfe..f223a02 100755
255--- a/t/t2006-checkout-index-basic.sh
256+++ b/t/t2006-checkout-index-basic.sh
257@@ -21,4 +21,44 @@ test_expect_success 'checkout-index -h in broken repository' '
258 test_i18ngrep "[Uu]sage" broken/usage
259 '
260
261+for mode in 'case' 'utf-8'
262+do
263+ case "$mode" in
264+ case) dir='A' symlink='a' mode_prereq='CASE_INSENSITIVE_FS' ;;
265+ utf-8)
266+ dir=$(printf "\141\314\210") symlink=$(printf "\303\244")
267+ mode_prereq='UTF8_NFD_TO_NFC' ;;
268+ esac
269+
270+ test_expect_success SYMLINKS,$mode_prereq \
271+ "checkout-index with $mode-collision don't write to the wrong place" '
272+ git init $mode-collision &&
273+ (
274+ cd $mode-collision &&
275+ mkdir target-dir &&
276+ empty_obj_hex=$(git hash-object -w --stdin </dev/null) &&
277+ symlink_hex=$(printf "%s" "$PWD/target-dir" | git hash-object -w --stdin) &&
278+ cat >objs <<-EOF &&
279+ 100644 blob ${empty_obj_hex} ${dir}/x
280+ 100644 blob ${empty_obj_hex} ${dir}/y
281+ 100644 blob ${empty_obj_hex} ${dir}/z
282+ 120000 blob ${symlink_hex} ${symlink}
283+ EOF
284+ git update-index --index-info <objs &&
285+ # Note: the order is important here to exercise the
286+ # case where the file at ${dir} has its type changed by
287+ # the time Git tries to check out ${dir}/z.
288+ #
289+ # Also, we use core.precomposeUnicode=false because we
290+ # want Git to treat the UTF-8 paths transparently on
291+ # Mac OS, matching what is in the index.
292+ #
293+ git -c core.precomposeUnicode=false checkout-index -f \
294+ ${dir}/x ${dir}/y ${symlink} ${dir}/z &&
295+ # Should not create ${dir}/z at ${symlink}/z
296+ test_path_is_missing target-dir/z
297+ )
298+ '
299+done
300+
301 test_done
302--
3032.17.1
304