summaryrefslogtreecommitdiffstats
path: root/meta
diff options
context:
space:
mode:
authorMinjae Kim <flowergom@gmail.com>2021-03-27 15:21:39 +0900
committerRichard Purdie <richard.purdie@linuxfoundation.org>2021-04-06 22:45:36 +0100
commitf0fdeea6657f0fdb9224ca25c0f01e0e1c724427 (patch)
tree1328a4a92cc8764f767ba2771c11c016e297a25d /meta
parent4ea2ccd7e9f74df8346e35022dea107f4e00ef86 (diff)
downloadpoky-f0fdeea6657f0fdb9224ca25c0f01e0e1c724427.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: 8293d5d1529629bd13028bdde1fa99da30313bac) Signed-off-by: Minjae Kim <flowergom@gmail.com> Signed-off-by: Steve Sakoman <steve@sakoman.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
-rw-r--r--meta/recipes-devtools/git/files/CVE-2021-21300.patch305
-rw-r--r--meta/recipes-devtools/git/git.inc4
2 files changed, 308 insertions, 1 deletions
diff --git a/meta/recipes-devtools/git/files/CVE-2021-21300.patch b/meta/recipes-devtools/git/files/CVE-2021-21300.patch
new file mode 100644
index 0000000000..9206f711cf
--- /dev/null
+++ b/meta/recipes-devtools/git/files/CVE-2021-21300.patch
@@ -0,0 +1,305 @@
1From 0e9cef2414f0df3fa5b9b56ff9072aa122bef29c Mon Sep 17 00:00:00 2001
2From: Minjae Kim <flowrgom@gmail.com>
3Date: Sat, 27 Mar 2021 15:18:46 +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 | 25 +++++++++++++++++++++
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, 130 insertions(+), 3 deletions(-)
78
79diff --git a/cache.h b/cache.h
80index 04cabaa..dda373f 100644
81--- a/cache.h
82+++ b/cache.h
83@@ -1675,6 +1675,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 bd24d91..cea9c72 100644
93--- a/compat/mingw.c
94+++ b/compat/mingw.c
95@@ -340,6 +340,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 d0dd9c0..a1ecfd3 100644
106--- a/git-compat-util.h
107+++ b/git-compat-util.h
108@@ -365,6 +365,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..ae3c665 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,21 @@ void remove_scheduled_dirs(void)
139 {
140 do_remove_scheduled_dirs(0);
141 }
142+
143+
144+void invalidate_lstat_cache(void)
145+{
146+ reset_lstat_cache(&default_cache);
147+}
148+
149+#undef rmdir
150+int lstat_cache_aware_rmdir(const char *path)
151+{
152+ /* Any change in this function must be made also in `mingw_rmdir()` */
153+ int ret = rmdir(path);
154+
155+ if (!ret)
156+ invalidate_lstat_cache();
157+
158+ return ret;
159+}
160diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
161index c954c70..6a1d5f6 100755
162--- a/t/t0021-conversion.sh
163+++ b/t/t0021-conversion.sh
164@@ -820,4 +820,43 @@ test_expect_success PERL 'invalid file in delayed checkout' '
165 grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log
166 '
167
168+for mode in 'case' 'utf-8'
169+do
170+ case "$mode" in
171+ case) dir='A' symlink='a' mode_prereq='CASE_INSENSITIVE_FS' ;;
172+ utf-8)
173+ dir=$(printf "\141\314\210") symlink=$(printf "\303\244")
174+ mode_prereq='UTF8_NFD_TO_NFC' ;;
175+ esac
176+
177+ test_expect_success PERL,SYMLINKS,$mode_prereq \
178+ "delayed checkout with $mode-collision don't write to the wrong place" '
179+ test_config_global filter.delay.process \
180+ "\"$TEST_ROOT/rot13-filter.pl\" --always-delay delayed.log clean smudge delay" &&
181+ test_config_global filter.delay.required true &&
182+ git init $mode-collision &&
183+ (
184+ cd $mode-collision &&
185+ mkdir target-dir &&
186+ empty_oid=$(printf "" | git hash-object -w --stdin) &&
187+ symlink_oid=$(printf "%s" "$PWD/target-dir" | git hash-object -w --stdin) &&
188+ attr_oid=$(echo "$dir/z filter=delay" | git hash-object -w --stdin) &&
189+ cat >objs <<-EOF &&
190+ 100644 blob $empty_oid $dir/x
191+ 100644 blob $empty_oid $dir/y
192+ 100644 blob $empty_oid $dir/z
193+ 120000 blob $symlink_oid $symlink
194+ 100644 blob $attr_oid .gitattributes
195+ EOF
196+ git update-index --index-info <objs &&
197+ git commit -m "test commit"
198+ ) &&
199+ git clone $mode-collision $mode-collision-cloned &&
200+ # Make sure z was really delayed
201+ grep "IN: smudge $dir/z .* \\[DELAYED\\]" $mode-collision-cloned/delayed.log &&
202+ # Should not create $dir/z at $symlink/z
203+ test_path_is_missing $mode-collision/target-dir/z
204+ '
205+done
206+
207 test_done
208diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
209index 4701072..007f2d7 100644
210--- a/t/t0021/rot13-filter.pl
211+++ b/t/t0021/rot13-filter.pl
212@@ -2,9 +2,15 @@
213 # Example implementation for the Git filter protocol version 2
214 # See Documentation/gitattributes.txt, section "Filter Protocol"
215 #
216-# The first argument defines a debug log file that the script write to.
217-# All remaining arguments define a list of supported protocol
218-# capabilities ("clean", "smudge", etc).
219+# Usage: rot13-filter.pl [--always-delay] <log path> <capabilities>
220+#
221+# Log path defines a debug log file that the script writes to. The
222+# subsequent arguments define a list of supported protocol capabilities
223+# ("clean", "smudge", etc).
224+#
225+# When --always-delay is given all pathnames with the "can-delay" flag
226+# that don't appear on the list bellow are delayed with a count of 1
227+# (see more below).
228 #
229 # This implementation supports special test cases:
230 # (1) If data with the pathname "clean-write-fail.r" is processed with
231@@ -53,6 +59,13 @@ sub gitperllib {
232 use Git::Packet;
233
234 my $MAX_PACKET_CONTENT_SIZE = 65516;
235+
236+my $always_delay = 0;
237+if ( $ARGV[0] eq '--always-delay' ) {
238+ $always_delay = 1;
239+ shift @ARGV;
240+}
241+
242 my $log_file = shift @ARGV;
243 my @capabilities = @ARGV;
244
245@@ -134,6 +147,8 @@ sub rot13 {
246 if ( $buffer eq "can-delay=1" ) {
247 if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
248 $DELAY{$pathname}{"requested"} = 1;
249+ } elsif ( !exists $DELAY{$pathname} and $always_delay ) {
250+ $DELAY{$pathname} = { "requested" => 1, "count" => 1 };
251 }
252 } else {
253 die "Unknown message '$buffer'";
254diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
255index 57cbdfe..f223a02 100755
256--- a/t/t2006-checkout-index-basic.sh
257+++ b/t/t2006-checkout-index-basic.sh
258@@ -21,4 +21,44 @@ test_expect_success 'checkout-index -h in broken repository' '
259 test_i18ngrep "[Uu]sage" broken/usage
260 '
261
262+for mode in 'case' 'utf-8'
263+do
264+ case "$mode" in
265+ case) dir='A' symlink='a' mode_prereq='CASE_INSENSITIVE_FS' ;;
266+ utf-8)
267+ dir=$(printf "\141\314\210") symlink=$(printf "\303\244")
268+ mode_prereq='UTF8_NFD_TO_NFC' ;;
269+ esac
270+
271+ test_expect_success SYMLINKS,$mode_prereq \
272+ "checkout-index with $mode-collision don't write to the wrong place" '
273+ git init $mode-collision &&
274+ (
275+ cd $mode-collision &&
276+ mkdir target-dir &&
277+ empty_obj_hex=$(git hash-object -w --stdin </dev/null) &&
278+ symlink_hex=$(printf "%s" "$PWD/target-dir" | git hash-object -w --stdin) &&
279+ cat >objs <<-EOF &&
280+ 100644 blob ${empty_obj_hex} ${dir}/x
281+ 100644 blob ${empty_obj_hex} ${dir}/y
282+ 100644 blob ${empty_obj_hex} ${dir}/z
283+ 120000 blob ${symlink_hex} ${symlink}
284+ EOF
285+ git update-index --index-info <objs &&
286+ # Note: the order is important here to exercise the
287+ # case where the file at ${dir} has its type changed by
288+ # the time Git tries to check out ${dir}/z.
289+ #
290+ # Also, we use core.precomposeUnicode=false because we
291+ # want Git to treat the UTF-8 paths transparently on
292+ # Mac OS, matching what is in the index.
293+ #
294+ git -c core.precomposeUnicode=false checkout-index -f \
295+ ${dir}/x ${dir}/y ${symlink} ${dir}/z &&
296+ # Should not create ${dir}/z at ${symlink}/z
297+ test_path_is_missing target-dir/z
298+ )
299+ '
300+done
301+
302 test_done
303--
3042.17.1
305
diff --git a/meta/recipes-devtools/git/git.inc b/meta/recipes-devtools/git/git.inc
index ae463061d8..738a429875 100644
--- a/meta/recipes-devtools/git/git.inc
+++ b/meta/recipes-devtools/git/git.inc
@@ -8,7 +8,9 @@ DEPENDS = "openssl curl zlib expat"
8PROVIDES_append_class-native = " git-replacement-native" 8PROVIDES_append_class-native = " git-replacement-native"
9 9
10SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \ 10SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \
11 ${KERNELORG_MIRROR}/software/scm/git/git-manpages-${PV}.tar.gz;name=manpages" 11 ${KERNELORG_MIRROR}/software/scm/git/git-manpages-${PV}.tar.gz;name=manpages \
12 file://CVE-2021-21300.patch \
13"
12 14
13S = "${WORKDIR}/git-${PV}" 15S = "${WORKDIR}/git-${PV}"
14 16