summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/git/files/CVE-2022-23521.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/git/files/CVE-2022-23521.patch')
-rw-r--r--meta/recipes-devtools/git/files/CVE-2022-23521.patch367
1 files changed, 367 insertions, 0 deletions
diff --git a/meta/recipes-devtools/git/files/CVE-2022-23521.patch b/meta/recipes-devtools/git/files/CVE-2022-23521.patch
new file mode 100644
index 0000000000..974546013d
--- /dev/null
+++ b/meta/recipes-devtools/git/files/CVE-2022-23521.patch
@@ -0,0 +1,367 @@
1From eb22e7dfa23da6bd9aed9bd1dad69e1e8e167d24 Mon Sep 17 00:00:00 2001
2From: Patrick Steinhardt <ps@pks.im>
3Date: Thu, 1 Dec 2022 15:45:15 +0100
4Subject: [PATCH] CVE-2022-23521
5
6attr: fix overflow when upserting attribute with overly long name
7
8The function `git_attr_internal()` is called to upsert attributes into
9the global map. And while all callers pass a `size_t`, the function
10itself accepts an `int` as the attribute name's length. This can lead to
11an integer overflow in case the attribute name is longer than `INT_MAX`.
12
13Now this overflow seems harmless as the first thing we do is to call
14`attr_name_valid()`, and that function only succeeds in case all chars
15in the range of `namelen` match a certain small set of chars. We thus
16can't do an out-of-bounds read as NUL is not part of that set and all
17strings passed to this function are NUL-terminated. And furthermore, we
18wouldn't ever read past the current attribute name anyway due to the
19same reason. And if validation fails we will return early.
20
21On the other hand it feels fragile to rely on this behaviour, even more
22so given that we pass `namelen` to `FLEX_ALLOC_MEM()`. So let's instead
23just do the correct thing here and accept a `size_t` as line length.
24
25Upstream-Status: Backport [https://github.com/git/git/commit/eb22e7dfa23da6bd9aed9bd1dad69e1e8e167d24 &https://github.com/git/git/commit/8d0d48cf2157cfb914db1f53b3fe40785b86f3aa & https://github.com/git/git/commit/24557209500e6ed618f04a8795a111a0c491a29c & https://github.com/git/git/commit/34ace8bad02bb14ecc5b631f7e3daaa7a9bba7d9 & https://github.com/git/git/commit/447ac906e189535e77dcb1f4bbe3f1bc917d4c12 & https://github.com/git/git/commit/e1e12e97ac73ded85f7d000da1063a774b3cc14f & https://github.com/git/git/commit/a60a66e409c265b2944f18bf43581c146812586d & https://github.com/git/git/commit/d74b1fd54fdbc45966d12ea907dece11e072fb2b & https://github.com/git/git/commit/dfa6b32b5e599d97448337ed4fc18dd50c90758f & https://github.com/git/git/commit/3c50032ff5289cc45659f21949c8d09e52164579
26
27CVE: CVE-2022-23521
28
29Reviewed-by: Sylvain Beucler <beuc@debian.org>
30Signed-off-by: Patrick Steinhardt <ps@pks.im>
31Signed-off-by: Junio C Hamano <gitster@pobox.com>
32Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
33---
34 attr.c | 97 +++++++++++++++++++++++++++----------------
35 attr.h | 12 ++++++
36 t/t0003-attributes.sh | 59 ++++++++++++++++++++++++++
37 3 files changed, 132 insertions(+), 36 deletions(-)
38
39diff --git a/attr.c b/attr.c
40index 11f19b5..63484ab 100644
41--- a/attr.c
42+++ b/attr.c
43@@ -29,7 +29,7 @@ static const char git_attr__unknown[] = "(builtin)unknown";
44 #endif
45
46 struct git_attr {
47- int attr_nr; /* unique attribute number */
48+ unsigned int attr_nr; /* unique attribute number */
49 char name[FLEX_ARRAY]; /* attribute name */
50 };
51
52@@ -221,7 +221,7 @@ static void report_invalid_attr(const char *name, size_t len,
53 * dictionary. If no entry is found, create a new attribute and store it in
54 * the dictionary.
55 */
56-static const struct git_attr *git_attr_internal(const char *name, int namelen)
57+static const struct git_attr *git_attr_internal(const char *name, size_t namelen)
58 {
59 struct git_attr *a;
60
61@@ -237,8 +237,8 @@ static const struct git_attr *git_attr_internal(const char *name, int namelen)
62 a->attr_nr = hashmap_get_size(&g_attr_hashmap.map);
63
64 attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a);
65- assert(a->attr_nr ==
66- (hashmap_get_size(&g_attr_hashmap.map) - 1));
67+ if (a->attr_nr != hashmap_get_size(&g_attr_hashmap.map) - 1)
68+ die(_("unable to add additional attribute"));
69 }
70
71 hashmap_unlock(&g_attr_hashmap);
72@@ -283,7 +283,7 @@ struct match_attr {
73 const struct git_attr *attr;
74 } u;
75 char is_macro;
76- unsigned num_attr;
77+ size_t num_attr;
78 struct attr_state state[FLEX_ARRAY];
79 };
80
81@@ -300,7 +300,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
82 struct attr_state *e)
83 {
84 const char *ep, *equals;
85- int len;
86+ size_t len;
87
88 ep = cp + strcspn(cp, blank);
89 equals = strchr(cp, '=');
90@@ -344,8 +344,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
91 static struct match_attr *parse_attr_line(const char *line, const char *src,
92 int lineno, int macro_ok)
93 {
94- int namelen;
95- int num_attr, i;
96+ size_t namelen, num_attr, i;
97 const char *cp, *name, *states;
98 struct match_attr *res = NULL;
99 int is_macro;
100@@ -356,6 +355,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
101 return NULL;
102 name = cp;
103
104+ if (strlen(line) >= ATTR_MAX_LINE_LENGTH) {
105+ warning(_("ignoring overly long attributes line %d"), lineno);
106+ return NULL;
107+ }
108+
109 if (*cp == '"' && !unquote_c_style(&pattern, name, &states)) {
110 name = pattern.buf;
111 namelen = pattern.len;
112@@ -392,10 +396,9 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
113 goto fail_return;
114 }
115
116- res = xcalloc(1,
117- sizeof(*res) +
118- sizeof(struct attr_state) * num_attr +
119- (is_macro ? 0 : namelen + 1));
120+ res = xcalloc(1, st_add3(sizeof(*res),
121+ st_mult(sizeof(struct attr_state), num_attr),
122+ is_macro ? 0 : namelen + 1));
123 if (is_macro) {
124 res->u.attr = git_attr_internal(name, namelen);
125 } else {
126@@ -458,11 +461,12 @@ struct attr_stack {
127
128 static void attr_stack_free(struct attr_stack *e)
129 {
130- int i;
131+ unsigned i;
132 free(e->origin);
133 for (i = 0; i < e->num_matches; i++) {
134 struct match_attr *a = e->attrs[i];
135- int j;
136+ size_t j;
137+
138 for (j = 0; j < a->num_attr; j++) {
139 const char *setto = a->state[j].setto;
140 if (setto == ATTR__TRUE ||
141@@ -671,8 +675,8 @@ static void handle_attr_line(struct attr_stack *res,
142 a = parse_attr_line(line, src, lineno, macro_ok);
143 if (!a)
144 return;
145- ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc);
146- res->attrs[res->num_matches++] = a;
147+ ALLOC_GROW_BY(res->attrs, res->num_matches, 1, res->alloc);
148+ res->attrs[res->num_matches - 1] = a;
149 }
150
151 static struct attr_stack *read_attr_from_array(const char **list)
152@@ -711,21 +715,37 @@ void git_attr_set_direction(enum git_attr_direction new_direction)
153
154 static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
155 {
156+ struct strbuf buf = STRBUF_INIT;
157 FILE *fp = fopen_or_warn(path, "r");
158 struct attr_stack *res;
159- char buf[2048];
160 int lineno = 0;
161+ int fd;
162+ struct stat st;
163
164 if (!fp)
165 return NULL;
166- res = xcalloc(1, sizeof(*res));
167- while (fgets(buf, sizeof(buf), fp)) {
168- char *bufp = buf;
169- if (!lineno)
170- skip_utf8_bom(&bufp, strlen(bufp));
171- handle_attr_line(res, bufp, path, ++lineno, macro_ok);
172+
173+ fd = fileno(fp);
174+ if (fstat(fd, &st)) {
175+ warning_errno(_("cannot fstat gitattributes file '%s'"), path);
176+ fclose(fp);
177+ return NULL;
178 }
179+ if (st.st_size >= ATTR_MAX_FILE_SIZE) {
180+ warning(_("ignoring overly large gitattributes file '%s'"), path);
181+ fclose(fp);
182+ return NULL;
183+ }
184+
185+ CALLOC_ARRAY(res, 1);
186+ while (strbuf_getline(&buf, fp) != EOF) {
187+ if (!lineno && starts_with(buf.buf, utf8_bom))
188+ strbuf_remove(&buf, 0, strlen(utf8_bom));
189+ handle_attr_line(res, buf.buf, path, ++lineno, macro_ok);
190+ }
191+
192 fclose(fp);
193+ strbuf_release(&buf);
194 return res;
195 }
196
197@@ -736,13 +756,18 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate,
198 struct attr_stack *res;
199 char *buf, *sp;
200 int lineno = 0;
201+ size_t size;
202
203 if (!istate)
204 return NULL;
205
206- buf = read_blob_data_from_index(istate, path, NULL);
207+ buf = read_blob_data_from_index(istate, path, &size);
208 if (!buf)
209 return NULL;
210+ if (size >= ATTR_MAX_FILE_SIZE) {
211+ warning(_("ignoring overly large gitattributes blob '%s'"), path);
212+ return NULL;
213+ }
214
215 res = xcalloc(1, sizeof(*res));
216 for (sp = buf; *sp; ) {
217@@ -1012,12 +1037,12 @@ static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
218 static int fill_one(const char *what, struct all_attrs_item *all_attrs,
219 const struct match_attr *a, int rem)
220 {
221- int i;
222+ size_t i;
223
224- for (i = a->num_attr - 1; rem > 0 && i >= 0; i--) {
225- const struct git_attr *attr = a->state[i].attr;
226+ for (i = a->num_attr; rem > 0 && i > 0; i--) {
227+ const struct git_attr *attr = a->state[i - 1].attr;
228 const char **n = &(all_attrs[attr->attr_nr].value);
229- const char *v = a->state[i].setto;
230+ const char *v = a->state[i - 1].setto;
231
232 if (*n == ATTR__UNKNOWN) {
233 debug_set(what,
234@@ -1036,11 +1061,11 @@ static int fill(const char *path, int pathlen, int basename_offset,
235 struct all_attrs_item *all_attrs, int rem)
236 {
237 for (; rem > 0 && stack; stack = stack->prev) {
238- int i;
239+ unsigned i;
240 const char *base = stack->origin ? stack->origin : "";
241
242- for (i = stack->num_matches - 1; 0 < rem && 0 <= i; i--) {
243- const struct match_attr *a = stack->attrs[i];
244+ for (i = stack->num_matches; 0 < rem && 0 < i; i--) {
245+ const struct match_attr *a = stack->attrs[i - 1];
246 if (a->is_macro)
247 continue;
248 if (path_matches(path, pathlen, basename_offset,
249@@ -1071,11 +1096,11 @@ static void determine_macros(struct all_attrs_item *all_attrs,
250 const struct attr_stack *stack)
251 {
252 for (; stack; stack = stack->prev) {
253- int i;
254- for (i = stack->num_matches - 1; i >= 0; i--) {
255- const struct match_attr *ma = stack->attrs[i];
256+ unsigned i;
257+ for (i = stack->num_matches; i > 0; i--) {
258+ const struct match_attr *ma = stack->attrs[i - 1];
259 if (ma->is_macro) {
260- int n = ma->u.attr->attr_nr;
261+ unsigned int n = ma->u.attr->attr_nr;
262 if (!all_attrs[n].macro) {
263 all_attrs[n].macro = ma;
264 }
265@@ -1127,7 +1152,7 @@ void git_check_attr(const struct index_state *istate,
266 collect_some_attrs(istate, path, check);
267
268 for (i = 0; i < check->nr; i++) {
269- size_t n = check->items[i].attr->attr_nr;
270+ unsigned int n = check->items[i].attr->attr_nr;
271 const char *value = check->all_attrs[n].value;
272 if (value == ATTR__UNKNOWN)
273 value = ATTR__UNSET;
274diff --git a/attr.h b/attr.h
275index b0378bf..f424285 100644
276--- a/attr.h
277+++ b/attr.h
278@@ -1,6 +1,18 @@
279 #ifndef ATTR_H
280 #define ATTR_H
281
282+/**
283+ * The maximum line length for a gitattributes file. If the line exceeds this
284+ * length we will ignore it.
285+ */
286+#define ATTR_MAX_LINE_LENGTH 2048
287+
288+ /**
289+ * The maximum size of the giattributes file. If the file exceeds this size we
290+ * will ignore it.
291+ */
292+#define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
293+
294 struct index_state;
295
296 /* An attribute is a pointer to this opaque structure */
297diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
298index 71e63d8..556245b 100755
299--- a/t/t0003-attributes.sh
300+++ b/t/t0003-attributes.sh
301@@ -342,4 +342,63 @@ test_expect_success 'query binary macro directly' '
302 test_cmp expect actual
303 '
304
305+test_expect_success 'large attributes line ignored in tree' '
306+ test_when_finished "rm .gitattributes" &&
307+ printf "path %02043d" 1 >.gitattributes &&
308+ git check-attr --all path >actual 2>err &&
309+ echo "warning: ignoring overly long attributes line 1" >expect &&
310+ test_cmp expect err &&
311+ test_must_be_empty actual
312+'
313+
314+test_expect_success 'large attributes line ignores trailing content in tree' '
315+ test_when_finished "rm .gitattributes" &&
316+ # older versions of Git broke lines at 2048 bytes; the 2045 bytes
317+ # of 0-padding here is accounting for the three bytes of "a 1", which
318+ # would knock "trailing" to the "next" line, where it would be
319+ # erroneously parsed.
320+ printf "a %02045dtrailing attribute\n" 1 >.gitattributes &&
321+ git check-attr --all trailing >actual 2>err &&
322+ echo "warning: ignoring overly long attributes line 1" >expect &&
323+ test_cmp expect err &&
324+ test_must_be_empty actual
325+'
326+
327+test_expect_success EXPENSIVE 'large attributes file ignored in tree' '
328+ test_when_finished "rm .gitattributes" &&
329+ dd if=/dev/zero of=.gitattributes bs=101M count=1 2>/dev/null &&
330+ git check-attr --all path >/dev/null 2>err &&
331+ echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect &&
332+ test_cmp expect err
333+'
334+
335+test_expect_success 'large attributes line ignored in index' '
336+ test_when_finished "git update-index --remove .gitattributes" &&
337+ blob=$(printf "path %02043d" 1 | git hash-object -w --stdin) &&
338+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
339+ git check-attr --cached --all path >actual 2>err &&
340+ echo "warning: ignoring overly long attributes line 1" >expect &&
341+ test_cmp expect err &&
342+ test_must_be_empty actual
343+'
344+
345+test_expect_success 'large attributes line ignores trailing content in index' '
346+ test_when_finished "git update-index --remove .gitattributes" &&
347+ blob=$(printf "a %02045dtrailing attribute\n" 1 | git hash-object -w --stdin) &&
348+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
349+ git check-attr --cached --all trailing >actual 2>err &&
350+ echo "warning: ignoring overly long attributes line 1" >expect &&
351+ test_cmp expect err &&
352+ test_must_be_empty actual
353+'
354+
355+test_expect_success EXPENSIVE 'large attributes file ignored in index' '
356+ test_when_finished "git update-index --remove .gitattributes" &&
357+ blob=$(dd if=/dev/zero bs=101M count=1 2>/dev/null | git hash-object -w --stdin) &&
358+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
359+ git check-attr --cached --all path >/dev/null 2>err &&
360+ echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect &&
361+ test_cmp expect err
362+'
363+
364 test_done
365--
3662.25.1
367