From 68e6c8678757ea1e02f70133d3b861814e681b92 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Feb 28 2023 09:06:25 +0000 Subject: import git-1.8.3.1-24.el7_9 --- diff --git a/SOURCES/git-cve-2022-23521.patch b/SOURCES/git-cve-2022-23521.patch new file mode 100644 index 0000000..8a93d08 --- /dev/null +++ b/SOURCES/git-cve-2022-23521.patch @@ -0,0 +1,571 @@ +diff -ur b/attr.c c/attr.c +--- b/attr.c 2013-06-10 22:01:55.000000000 +0200 ++++ c/attr.c 2023-02-21 12:25:20.735892607 +0100 +@@ -12,6 +12,7 @@ + #include "exec_cmd.h" + #include "attr.h" + #include "dir.h" ++#include "utf8.h" + + const char git_attr__true[] = "(builtin)true"; + const char git_attr__false[] = "\0(builtin)false"; +@@ -55,26 +56,36 @@ + return val; + } + +-static int invalid_attr_name(const char *name, int namelen) ++static int attr_name_valid(const char *name, size_t namelen) + { + /* + * Attribute name cannot begin with '-' and must consist of + * characters from [-A-Za-z0-9_.]. + */ + if (namelen <= 0 || *name == '-') +- return -1; ++ return 0; + while (namelen--) { + char ch = *name++; + if (! (ch == '-' || ch == '.' || ch == '_' || + ('0' <= ch && ch <= '9') || + ('a' <= ch && ch <= 'z') || + ('A' <= ch && ch <= 'Z')) ) +- return -1; ++ return 0; + } +- return 0; ++ return 1; + } + +-static struct git_attr *git_attr_internal(const char *name, int len) ++static void report_invalid_attr(const char *name, size_t len, ++ const char *src, int lineno) ++{ ++ struct strbuf err = STRBUF_INIT; ++ strbuf_addf(&err, _("%.*s is not a valid attribute name"), ++ (int) len, name); ++ fprintf(stderr, "%s: %s:%d\n", err.buf, src, lineno); ++ strbuf_release(&err); ++} ++ ++static struct git_attr *git_attr_internal(const char *name, size_t len) + { + unsigned hval = hash_name(name, len); + unsigned pos = hval % HASHSIZE; +@@ -86,7 +97,7 @@ + return a; + } + +- if (invalid_attr_name(name, len)) ++ if (!attr_name_valid(name, len)) + return NULL; + + a = xmalloc(sizeof(*a) + len + 1); +@@ -142,7 +153,7 @@ + struct git_attr *attr; + } u; + char is_macro; +- unsigned num_attr; ++ size_t num_attr; + struct attr_state state[FLEX_ARRAY]; + }; + +@@ -159,7 +170,7 @@ + struct attr_state *e) + { + const char *ep, *equals; +- int len; ++ size_t len; + + ep = cp + strcspn(cp, blank); + equals = strchr(cp, '='); +@@ -174,10 +185,8 @@ + cp++; + len--; + } +- if (invalid_attr_name(cp, len)) { +- fprintf(stderr, +- "%.*s is not a valid attribute name: %s:%d\n", +- len, cp, src, lineno); ++ if (!attr_name_valid(cp, len)) { ++ report_invalid_attr(cp, len, src, lineno); + return NULL; + } + } else { +@@ -199,8 +208,7 @@ + static struct match_attr *parse_attr_line(const char *line, const char *src, + int lineno, int macro_ok) + { +- int namelen; +- int num_attr, i; ++ size_t namelen, num_attr, i; + const char *cp, *name, *states; + struct match_attr *res = NULL; + int is_macro; +@@ -209,6 +217,12 @@ + if (!*cp || *cp == '#') + return NULL; + name = cp; ++ ++ if (strlen(line) >= ATTR_MAX_LINE_LENGTH) { ++ warning(_("ignoring overly long attributes line %d"), lineno); ++ return NULL; ++ } ++ + namelen = strcspn(name, blank); + if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && + !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) { +@@ -221,10 +235,8 @@ + name += strlen(ATTRIBUTE_MACRO_PREFIX); + name += strspn(name, blank); + namelen = strcspn(name, blank); +- if (invalid_attr_name(name, namelen)) { +- fprintf(stderr, +- "%.*s is not a valid attribute name: %s:%d\n", +- namelen, name, src, lineno); ++ if (!attr_name_valid(name, namelen)) { ++ report_invalid_attr(cp, namelen, src, lineno); + return NULL; + } + } +@@ -241,10 +253,9 @@ + return NULL; + } + +- res = xcalloc(1, +- sizeof(*res) + +- sizeof(struct attr_state) * num_attr + +- (is_macro ? 0 : namelen + 1)); ++ res = xcalloc(1, st_add3(sizeof(*res), ++ st_mult(sizeof(struct attr_state), num_attr), ++ is_macro ? 0 : namelen + 1)); + if (is_macro) + res->u.attr = git_attr_internal(name, namelen); + else { +@@ -301,11 +312,11 @@ + + static void free_attr_elem(struct attr_stack *e) + { +- int i; ++ unsigned i; + free(e->origin); + for (i = 0; i < e->num_matches; i++) { + struct match_attr *a = e->attrs[i]; +- int j; ++ size_t j; + for (j = 0; j < a->num_attr; j++) { + const char *setto = a->state[j].setto; + if (setto == ATTR__TRUE || +@@ -364,20 +375,39 @@ + + static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) + { ++ struct strbuf buf = STRBUF_INIT; + FILE *fp = fopen(path, "r"); + struct attr_stack *res; +- char buf[2048]; + int lineno = 0; ++ int fd; ++ struct stat st; + + if (!fp) { + if (errno != ENOENT && errno != ENOTDIR) + warn_on_inaccessible(path); + return NULL; + } ++ ++ fd = fileno(fp); ++ if (fstat(fd, &st)) { ++ warning_errno(_("cannot fstat gitattributes file '%s'"), path); ++ fclose(fp); ++ return NULL; ++ } ++ if (st.st_size >= ATTR_MAX_FILE_SIZE) { ++ warning(_("ignoring overly large gitattributes file '%s'"), path); ++ fclose(fp); ++ return NULL; ++ } ++ + res = xcalloc(1, sizeof(*res)); +- while (fgets(buf, sizeof(buf), fp)) +- handle_attr_line(res, buf, path, ++lineno, macro_ok); ++ while (strbuf_getline(&buf, fp, '\n') != EOF) { ++ if (!lineno && starts_with(buf.buf, utf8_bom)) ++ strbuf_remove(&buf, 0, strlen(utf8_bom)); ++ handle_attr_line(res, buf.buf, path, ++lineno, macro_ok); ++ } + fclose(fp); ++ strbuf_release(&buf); + return res; + } + +@@ -386,11 +416,18 @@ + struct attr_stack *res; + char *buf, *sp; + int lineno = 0; ++ unsigned long size; + +- buf = read_blob_data_from_index(use_index ? use_index : &the_index, path, NULL); ++ buf = read_blob_data_from_index(use_index ? use_index : &the_index, path, &size); + if (!buf) + return NULL; + ++ if (size >= ATTR_MAX_FILE_SIZE) { ++ warning(_("ignoring overly large gitattributes blob '%s'"), path); ++ return NULL; ++ } ++ ++ + res = xcalloc(1, sizeof(*res)); + for (sp = buf; *sp; ) { + char *ep; +@@ -648,15 +685,15 @@ + + static int macroexpand_one(int attr_nr, int rem); + +-static int fill_one(const char *what, struct match_attr *a, int rem) ++static int fill_one(const char *what, const struct match_attr *a, int rem) + { + struct git_attr_check *check = check_all_attr; +- int i; ++ size_t i; + +- for (i = a->num_attr - 1; 0 < rem && 0 <= i; i--) { +- struct git_attr *attr = a->state[i].attr; ++ for (i = a->num_attr; rem > 0 && i > 0; i--) { ++ const struct git_attr *attr = a->state[i - 1].attr; + const char **n = &(check[attr->attr_nr].value); +- const char *v = a->state[i].setto; ++ const char *v = a->state[i - 1].setto; + + if (*n == ATTR__UNKNOWN) { + debug_set(what, +@@ -673,11 +710,11 @@ + static int fill(const char *path, int pathlen, int basename_offset, + struct attr_stack *stk, int rem) + { +- int i; ++ unsigned i; + const char *base = stk->origin ? stk->origin : ""; + +- for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) { +- struct match_attr *a = stk->attrs[i]; ++ for (i = stk->num_matches; 0 < rem && 0 < i; i--) { ++ const struct match_attr *a = stk->attrs[i - 1]; + if (a->is_macro) + continue; + if (path_matches(path, pathlen, basename_offset, +@@ -691,14 +728,14 @@ + { + struct attr_stack *stk; + struct match_attr *a = NULL; +- int i; ++ unsigned i; + + if (check_all_attr[attr_nr].value != ATTR__TRUE) + return rem; + + for (stk = attr_stack; !a && stk; stk = stk->prev) +- for (i = stk->num_matches - 1; !a && 0 <= i; i--) { +- struct match_attr *ma = stk->attrs[i]; ++ for (i = stk->num_matches; !a && i > 0; i--) { ++ struct match_attr *ma = stk->attrs[i - 1]; + if (!ma->is_macro) + continue; + if (ma->u.attr->attr_nr == attr_nr) +diff -ur b/attr.h c/attr.h +--- b/attr.h 2013-06-10 22:01:55.000000000 +0200 ++++ c/attr.h 2023-02-21 12:25:42.455029765 +0100 +@@ -1,6 +1,18 @@ + #ifndef ATTR_H + #define ATTR_H + ++/** ++ * The maximum line length for a gitattributes file. If the line exceeds this ++ * length we will ignore it. ++ */ ++#define ATTR_MAX_LINE_LENGTH 2048 ++ ++ /** ++ * The maximum size of the giattributes file. If the file exceeds this size we ++ * will ignore it. ++ */ ++#define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024) ++ + /* An attribute is a pointer to this opaque structure */ + struct git_attr; + +diff -ur b/git-compat-util.h c/git-compat-util.h +--- b/git-compat-util.h 2023-02-21 11:27:58.038145942 +0100 ++++ c/git-compat-util.h 2023-02-21 12:27:18.836638388 +0100 +@@ -324,7 +324,9 @@ + extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2))); + extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2))); + extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); ++extern int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2))); + extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); ++extern void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2))); + + /* + * Let callers be aware of the constant return value; this can help +@@ -524,8 +526,8 @@ + (uintmax_t)a, (uintmax_t)b); + return a + b; + } +-#define st_add3(a,b,c) st_add((a),st_add((b),(c))) +-#define st_add4(a,b,c,d) st_add((a),st_add3((b),(c),(d))) ++#define st_add3(a,b,c) st_add(st_add((a),(b)),(c)) ++#define st_add4(a,b,c,d) st_add(st_add3((a),(b),(c)),(d)) + + static inline size_t st_mult(size_t a, size_t b) + { +diff -ur b/t/t0003-attributes.sh c/t/t0003-attributes.sh +--- b/t/t0003-attributes.sh 2013-06-10 22:01:55.000000000 +0200 ++++ c/t/t0003-attributes.sh 2023-02-21 12:30:22.614804084 +0100 +@@ -245,39 +245,106 @@ + ' + + test_expect_success 'setup bare' ' +- git clone --bare . bare.git && +- cd bare.git ++ git clone --bare . bare.git + ' + + test_expect_success 'bare repository: check that .gitattribute is ignored' ' + ( +- echo "f test=f" +- echo "a/i test=a/i" +- ) >.gitattributes && +- attr_check f unspecified && +- attr_check a/f unspecified && +- attr_check a/c/f unspecified && +- attr_check a/i unspecified && +- attr_check subdir/a/i unspecified ++ cd bare.git && ++ ( ++ echo "f test=f" ++ echo "a/i test=a/i" ++ ) >.gitattributes && ++ attr_check f unspecified && ++ attr_check a/f unspecified && ++ attr_check a/c/f unspecified && ++ attr_check a/i unspecified && ++ attr_check subdir/a/i unspecified ++ ) + ' + + test_expect_success 'bare repository: check that --cached honors index' ' +- GIT_INDEX_FILE=../.git/index \ +- git check-attr --cached --stdin --all <../stdin-all | +- sort >actual && +- test_cmp ../specified-all actual ++ ( ++ cd bare.git && ++ GIT_INDEX_FILE=../.git/index \ ++ git check-attr --cached --stdin --all <../stdin-all | ++ sort >actual && ++ test_cmp ../specified-all actual ++ ) + ' + + test_expect_success 'bare repository: test info/attributes' ' + ( +- echo "f test=f" +- echo "a/i test=a/i" +- ) >info/attributes && +- attr_check f f && +- attr_check a/f f && +- attr_check a/c/f f && +- attr_check a/i a/i && +- attr_check subdir/a/i unspecified ++ cd bare.git && ++ ( ++ echo "f test=f" ++ echo "a/i test=a/i" ++ ) >info/attributes && ++ attr_check f f && ++ attr_check a/f f && ++ attr_check a/c/f f && ++ attr_check a/i a/i && ++ attr_check subdir/a/i unspecified ++ ) ++' ++ ++test_expect_success 'large attributes line ignored in tree' ' ++ test_when_finished "rm .gitattributes" && ++ printf "path %02043d" 1 >.gitattributes && ++ git check-attr --all path >actual 2>err && ++ echo "warning: ignoring overly long attributes line 1" >expect && ++ test_cmp expect err && ++ test_must_be_empty actual ++' ++ ++test_expect_success 'large attributes line ignores trailing content in tree' ' ++ test_when_finished "rm .gitattributes" && ++ # older versions of Git broke lines at 2048 bytes; the 2045 bytes ++ # of 0-padding here is accounting for the three bytes of "a 1", which ++ # would knock "trailing" to the "next" line, where it would be ++ # erroneously parsed. ++ printf "a %02045dtrailing attribute\n" 1 >.gitattributes && ++ git check-attr --all trailing >actual 2>err && ++ echo "warning: ignoring overly long attributes line 1" >expect && ++ test_cmp expect err && ++ test_must_be_empty actual ++' ++ ++test_expect_success EXPENSIVE 'large attributes file ignored in tree' ' ++ test_when_finished "rm .gitattributes" && ++ dd if=/dev/zero of=.gitattributes bs=101M count=1 2>/dev/null && ++ git check-attr --all path >/dev/null 2>err && ++ echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect && ++ test_cmp expect err ++' ++ ++test_expect_success 'large attributes line ignored in index' ' ++ test_when_finished "git update-index --remove .gitattributes" && ++ blob=$(printf "path %02043d" 1 | git hash-object -w --stdin) && ++ git update-index --add --cacheinfo 100644 $blob .gitattributes && ++ git check-attr --cached --all path >actual 2>err && ++ echo "warning: ignoring overly long attributes line 1" >expect && ++ test_cmp expect err && ++ test_must_be_empty actual ++' ++ ++test_expect_success 'large attributes line ignores trailing content in index' ' ++ test_when_finished "git update-index --remove .gitattributes" && ++ blob=$(printf "a %02045dtrailing attribute\n" 1 | git hash-object -w --stdin) && ++ git update-index --add --cacheinfo 100644 $blob .gitattributes && ++ git check-attr --cached --all trailing >actual 2>err && ++ echo "warning: ignoring overly long attributes line 1" >expect && ++ test_cmp expect err && ++ test_must_be_empty actual ++' ++ ++test_expect_success EXPENSIVE 'large attributes file ignored in index' ' ++ test_when_finished "git update-index --remove .gitattributes" && ++ blob=$(dd if=/dev/zero bs=101M count=1 2>/dev/null | git hash-object -w --stdin) && ++ git update-index --add --cacheinfo 100644 $blob .gitattributes && ++ git check-attr --cached --all path >/dev/null 2>err && ++ echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect && ++ test_cmp expect err + ' + + test_done +diff -ur b/t/test-lib-functions.sh c/t/test-lib-functions.sh +--- b/t/test-lib-functions.sh 2013-06-10 22:01:55.000000000 +0200 ++++ c/t/test-lib-functions.sh 2023-02-21 12:31:24.357204323 +0100 +@@ -609,6 +609,20 @@ + $GIT_TEST_CMP "$@" + } + ++# Check if the file expected to be empty is indeed empty, and barfs ++# otherwise. ++ ++test_must_be_empty () { ++ test "$#" -ne 1 && BUG "1 param" ++ test_path_is_file "$1" && ++ if test -s "$1" ++ then ++ echo "'$1' is not empty, it contains:" ++ cat "$1" ++ return 1 ++ fi ++} ++ + # Tests that its two parameters refer to the same revision + test_cmp_rev () { + git rev-parse --verify "$1" >expect.rev && +diff -ur b/t/test-lib.sh c/t/test-lib.sh +--- b/t/test-lib.sh 2023-02-21 11:52:24.739202530 +0100 ++++ c/t/test-lib.sh 2023-02-21 12:31:52.866389106 +0100 +@@ -153,6 +153,9 @@ + LF=' + ' + ++# Single quote ++SQ=\' ++ + export _x05 _x40 _z40 LF + + # Each test should start with something like this, after copyright notices: +diff -ur b/usage.c c/usage.c +--- b/usage.c 2013-06-10 22:01:55.000000000 +0200 ++++ c/usage.c 2023-02-21 12:32:56.807803579 +0100 +@@ -104,6 +104,30 @@ + va_end(params); + } + ++static const char *fmt_with_err(char *buf, int n, const char *fmt) ++{ ++ char str_error[256], *err; ++ int i, j; ++ ++ err = strerror(errno); ++ for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) { ++ if ((str_error[j++] = err[i++]) != '%') ++ continue; ++ if (j < sizeof(str_error) - 1) { ++ str_error[j++] = '%'; ++ } else { ++ /* No room to double the '%', so we overwrite it with ++ * '\0' below */ ++ j--; ++ break; ++ } ++ } ++ str_error[j] = 0; ++ /* Truncation is acceptable here */ ++ snprintf(buf, n, "%s: %s", fmt, str_error); ++ return buf; ++} ++ + void NORETURN die_errno(const char *fmt, ...) + { + va_list params; +@@ -149,6 +173,16 @@ + return -1; + } + ++void warning_errno(const char *warn, ...) ++{ ++ char buf[1024]; ++ va_list params; ++ ++ va_start(params, warn); ++ warn_routine(fmt_with_err(buf, sizeof(buf), warn), params); ++ va_end(params); ++} ++ + void warning(const char *warn, ...) + { + va_list params; +diff -ur b/utf8.c c/utf8.c +--- b/utf8.c 2023-02-21 12:00:28.555925285 +0100 ++++ c/utf8.c 2023-02-21 12:33:48.863141018 +0100 +@@ -639,3 +639,14 @@ + + return chrlen; + } ++ ++const char utf8_bom[] = "\357\273\277"; ++ ++int skip_utf8_bom(char **text, size_t len) ++{ ++ if (len < strlen(utf8_bom) || ++ memcmp(*text, utf8_bom, strlen(utf8_bom))) ++ return 0; ++ *text += strlen(utf8_bom); ++ return 1; ++} +diff -ur b/utf8.h c/utf8.h +--- b/utf8.h 2023-02-21 12:00:40.186991497 +0100 ++++ c/utf8.h 2023-02-21 12:34:19.536339834 +0100 +@@ -12,6 +12,9 @@ + int same_encoding(const char *, const char *); + int utf8_fprintf(FILE *, const char *, ...); + ++extern const char utf8_bom[]; ++int skip_utf8_bom(char **, size_t); ++ + void strbuf_add_wrapped_text(struct strbuf *buf, + const char *text, int indent, int indent2, int width); + void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len, diff --git a/SOURCES/git-cve-2022-41903.patch b/SOURCES/git-cve-2022-41903.patch new file mode 100644 index 0000000..de8cc01 --- /dev/null +++ b/SOURCES/git-cve-2022-41903.patch @@ -0,0 +1,401 @@ +diff -ur a/git-compat-util.h b/git-compat-util.h +--- a/git-compat-util.h 2023-02-01 16:15:48.875070563 +0100 ++++ b/git-compat-util.h 2023-02-21 11:27:58.038145942 +0100 +@@ -543,6 +543,13 @@ + return a - b; + } + ++static inline int cast_size_t_to_int(size_t a) ++{ ++ if (a > INT_MAX) ++ die("number too large to represent as int on this platform: %"PRIuMAX, ++ (uintmax_t)a); ++ return (int)a; ++} + + extern char *xstrdup(const char *str); + extern void *xmalloc(size_t size); +diff -ur a/pretty.c b/pretty.c +--- a/pretty.c 2013-06-10 22:01:55.000000000 +0200 ++++ b/pretty.c 2023-02-21 11:18:11.584775725 +0100 +@@ -11,6 +11,13 @@ + #include "reflog-walk.h" + #include "gpg-interface.h" + ++/* ++ * The limit for formatting directives, which enable the caller to append ++ * arbitrarily many bytes to the formatted buffer. This includes padding ++ * and wrapping formatters. ++ */ ++#define FORMATTING_LIMIT (16 * 1024) ++ + static char *user_format; + static struct cmt_fmt_map { + const char *name; +@@ -917,7 +924,9 @@ + if (pos) + strbuf_add(&tmp, sb->buf, pos); + strbuf_add_wrapped_text(&tmp, sb->buf + pos, +- (int) indent1, (int) indent2, (int) width); ++ cast_size_t_to_int(indent1), ++ cast_size_t_to_int(indent2), ++ cast_size_t_to_int(width)); + strbuf_swap(&tmp, sb); + strbuf_release(&tmp); + } +@@ -1030,9 +1039,18 @@ + const char *end = start + strcspn(start, ",)"); + char *next; + int width; +- if (!end || end == start) ++ if (!*end || end == start) + return 0; + width = strtoul(start, &next, 10); ++ ++ /* ++ * We need to limit the amount of padding, or otherwise this ++ * would allow the user to pad the buffer by arbitrarily many ++ * bytes and thus cause resource exhaustion. ++ */ ++ if (width < -FORMATTING_LIMIT || width > FORMATTING_LIMIT) ++ return 0; ++ + if (next == start || width == 0) + return 0; + c->padding = to_column ? -width : width; +@@ -1119,6 +1137,16 @@ + if (*next != ')') + return 0; + } ++ ++ /* ++ * We need to limit the format here as it allows the ++ * user to prepend arbitrarily many bytes to the buffer ++ * when rewrapping. ++ */ ++ if (width > FORMATTING_LIMIT || ++ indent1 > FORMATTING_LIMIT || ++ indent2 > FORMATTING_LIMIT) ++ return 0; + rewrap_message_tail(sb, c, width, indent1, indent2); + return end - placeholder + 1; + } else +@@ -1296,18 +1324,20 @@ + struct format_commit_context *c) + { + struct strbuf local_sb = STRBUF_INIT; +- int total_consumed = 0, len, padding = c->padding; ++ size_t total_consumed = 0; ++ int len, padding = c->padding; ++ + if (padding < 0) { + const char *start = strrchr(sb->buf, '\n'); + int occupied; + if (!start) + start = sb->buf; +- occupied = utf8_strnwidth(start, -1, 1); ++ occupied = utf8_strnwidth(start, strlen(start), 1); + padding = (-padding) - occupied; + } + while (1) { + int modifier = *placeholder == 'C'; +- int consumed = format_commit_one(&local_sb, placeholder, c); ++ size_t consumed = format_commit_one(&local_sb, placeholder, c); + total_consumed += consumed; + + if (!modifier) +@@ -1319,7 +1349,7 @@ + placeholder++; + total_consumed++; + } +- len = utf8_strnwidth(local_sb.buf, -1, 1); ++ len = utf8_strnwidth(local_sb.buf, local_sb.len, 1); + + if (c->flush_type == flush_left_and_steal) { + const char *ch = sb->buf + sb->len - 1; +@@ -1334,7 +1364,7 @@ + if (*ch != 'm') + break; + p = ch - 1; +- while (ch - p < 10 && *p != '\033') ++ while (p > sb->buf && ch - p < 10 && *p != '\033') + p--; + if (*p != '\033' || + ch + 1 - p != display_mode_esc_sequence_len(p)) +@@ -1373,7 +1403,7 @@ + } + strbuf_addstr(sb, local_sb.buf); + } else { +- int sb_len = sb->len, offset = 0; ++ size_t sb_len = sb->len, offset = 0; + if (c->flush_type == flush_left) + offset = padding - len; + else if (c->flush_type == flush_both) +@@ -1398,8 +1428,7 @@ + const char *placeholder, + void *context) + { +- int consumed; +- size_t orig_len; ++ size_t consumed, orig_len; + enum { + NO_MAGIC, + ADD_LF_BEFORE_NON_EMPTY, +@@ -1420,9 +1449,21 @@ + default: + break; + } +- if (magic != NO_MAGIC) ++ if (magic != NO_MAGIC) { + placeholder++; + ++ switch (placeholder[0]) { ++ case 'w': ++ /* ++ * `%+w()` cannot ever expand to a non-empty string, ++ * and it potentially changes the layout of preceding ++ * contents. We're thus not able to handle the magic in ++ * this combination and refuse the pattern. ++ */ ++ return 0; ++ }; ++ } ++ + orig_len = sb->len; + if (((struct format_commit_context *)context)->flush_type != no_flush) + consumed = format_and_pad_commit(sb, placeholder, context); +diff -ur a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh +--- a/t/t4205-log-pretty-formats.sh 2013-06-10 22:01:55.000000000 +0200 ++++ b/t/t4205-log-pretty-formats.sh 2023-02-21 11:19:31.345309067 +0100 +@@ -274,4 +274,80 @@ + test_cmp expected actual + ' + ++test_expect_success 'log --pretty with space stealing' ' ++ printf mm0 >expect && ++ git log -1 --pretty="format:mm%>>|(1)%x30" >actual && ++ test_cmp expect actual ++' ++ ++test_expect_success 'log --pretty with invalid padding format' ' ++ printf "%s%%<(20" "$(git rev-parse HEAD)" >expect && ++ git log -1 --pretty="format:%H%<(20" >actual && ++ test_cmp expect actual ++' ++ ++test_expect_success 'log --pretty with magical wrapping directives' ' ++ commit_id=$(git commit-tree HEAD^{tree} -m "describe me") && ++ git tag describe-me $commit_id && ++ printf "\n(tag:\ndescribe-me)%%+w(2)" >expect && ++ git log -1 --pretty="format:%w(1)%+d%+w(2)" $commit_id >actual && ++ test_cmp expect actual ++' ++ ++test_expect_success SIZE_T_IS_64BIT 'log --pretty with overflowing wrapping directive' ' ++ printf "%%w(2147483649,1,1)0" >expect && ++ git log -1 --pretty="format:%w(2147483649,1,1)%x30" >actual && ++ test_cmp expect actual && ++ printf "%%w(1,2147483649,1)0" >expect && ++ git log -1 --pretty="format:%w(1,2147483649,1)%x30" >actual && ++ test_cmp expect actual && ++ printf "%%w(1,1,2147483649)0" >expect && ++ git log -1 --pretty="format:%w(1,1,2147483649)%x30" >actual && ++ test_cmp expect actual ++' ++ ++test_expect_success SIZE_T_IS_64BIT 'log --pretty with overflowing padding directive' ' ++ printf "%%<(2147483649)0" >expect && ++ git log -1 --pretty="format:%<(2147483649)%x30" >actual && ++ test_cmp expect actual ++' ++ ++test_expect_success 'log --pretty with padding and preceding control chars' ' ++ printf "\20\20 0" >expect && ++ git log -1 --pretty="format:%x10%x10%>|(4)%x30" >actual && ++ test_cmp expect actual ++' ++ ++test_expect_success 'log --pretty truncation with control chars' ' ++ test_commit "$(printf "\20\20\20\20xxxx")" file contents commit-with-control-chars && ++ printf "\20\20\20\20x.." >expect && ++ git log -1 --pretty="format:%<(3,trunc)%s" commit-with-control-chars >actual && ++ test_cmp expect actual ++' ++ ++test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' ' ++ # We only assert that this command does not crash. This needs to be ++ # executed with the address sanitizer to demonstrate failure. ++ git log -1 --pretty="format:%>(2147483646)%x41%41%>(2147483646)%x41" >/dev/null ++' ++ ++test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'set up huge commit' ' ++ test-tool genzeros 2147483649 | tr "\000" "1" >expect && ++ huge_commit=$(git commit-tree -F expect HEAD^{tree}) ++' ++ ++test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' ' ++ git log -1 --format="%B%<(1)%x30" $huge_commit >actual && ++ echo 0 >>expect && ++ test_cmp expect actual ++' ++ ++test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message does not cause allocation failure' ' ++ test_must_fail git log -1 --format="%<(1)%B" $huge_commit 2>error && ++ cat >expect <<-EOF && ++ fatal: number too large to represent as int on this platform: 2147483649 ++ EOF ++ test_cmp expect error ++' ++ + test_done +diff -ur a/t/test-lib.sh b/t/test-lib.sh +--- a/t/test-lib.sh 2013-06-10 22:01:55.000000000 +0200 ++++ b/t/test-lib.sh 2023-02-21 11:52:24.739202530 +0100 +@@ -770,6 +770,10 @@ + git var GIT_AUTHOR_IDENT + ' + ++test_lazy_prereq SIZE_T_IS_64BIT ' ++ test 8 -eq "$(build_option sizeof-size_t)" ++' ++ + # When the tests are run as root, permission tests will report that + # things are writable when they shouldn't be. + test -w / || test_set_prereq SANITY +diff -ur a/utf8.c b/utf8.c +--- a/utf8.c 2013-06-10 22:01:55.000000000 +0200 ++++ b/utf8.c 2023-02-21 12:00:28.555925285 +0100 +@@ -266,26 +266,32 @@ + * string, assuming that the string is utf8. Returns strlen() instead + * if the string does not look like a valid utf8 string. + */ +-int utf8_strnwidth(const char *string, int len, int skip_ansi) ++int utf8_strnwidth(const char *string, size_t len, int skip_ansi) + { +- int width = 0; + const char *orig = string; ++ size_t width = 0; + +- if (len == -1) +- len = strlen(string); + while (string && string < orig + len) { +- int skip; ++ int glyph_width; ++ size_t skip; + while (skip_ansi && + (skip = display_mode_esc_sequence_len(string)) != 0) + string += skip; +- width += utf8_width(&string, NULL); ++ glyph_width = utf8_width(&string, NULL); ++ if (glyph_width > 0) ++ width += glyph_width; + } +- return string ? width : len; ++ ++ /* ++ * TODO: fix the interface of this function and `utf8_strwidth()` to ++ * return `size_t` instead of `int`. ++ */ ++ return cast_size_t_to_int(string ? width : len); + } + + int utf8_strwidth(const char *string) + { +- return utf8_strnwidth(string, -1, 0); ++ return utf8_strnwidth(string, strlen(string), 0); + } + + int is_utf8(const char *text) +@@ -424,47 +430,52 @@ + void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width, + const char *subst) + { +- struct strbuf sb_dst = STRBUF_INIT; +- char *src = sb_src->buf; +- char *end = src + sb_src->len; +- char *dst; +- int w = 0, subst_len = 0; +- +- if (subst) +- subst_len = strlen(subst); +- strbuf_grow(&sb_dst, sb_src->len + subst_len); +- dst = sb_dst.buf; ++ const char *src = sb_src->buf, *end = sb_src->buf + sb_src->len; ++ struct strbuf dst; ++ int w = 0; ++ ++ strbuf_init(&dst, sb_src->len); + + while (src < end) { +- char *old; ++ const char *old; ++ int glyph_width; + size_t n; + + while ((n = display_mode_esc_sequence_len(src))) { +- memcpy(dst, src, n); ++ strbuf_add(&dst, src, n); + src += n; +- dst += n; + } ++ ++ if (src >= end) ++ break; + + old = src; +- n = utf8_width((const char**)&src, NULL); +- if (!src) /* broken utf-8, do nothing */ +- return; +- if (n && w >= pos && w < pos + width) { ++ glyph_width = utf8_width((const char**)&src, NULL); ++ if (!src) /* broken utf-8, do nothing */ ++ goto out; ++ ++ /* ++ * In case we see a control character we copy it into the ++ * buffer, but don't add it to the width. ++ */ ++ if (glyph_width < 0) ++ glyph_width = 0; ++ ++ if (glyph_width && w >= pos && w < pos + width) { + if (subst) { +- memcpy(dst, subst, subst_len); +- dst += subst_len; ++ strbuf_addstr(&dst, subst); + subst = NULL; + } +- w += n; +- continue; ++ } else { ++ strbuf_add(&dst, old, src - old); + } +- memcpy(dst, old, src - old); +- dst += src - old; +- w += n; +- } +- strbuf_setlen(&sb_dst, dst - sb_dst.buf); +- strbuf_swap(sb_src, &sb_dst); +- strbuf_release(&sb_dst); ++ ++ w += glyph_width; ++ } ++ ++ strbuf_swap(sb_src, &dst); ++out: ++ strbuf_release(&dst); + } + + int is_encoding_utf8(const char *name) +diff -ur a/utf8.h b/utf8.h +--- a/utf8.h 2013-06-10 22:01:55.000000000 +0200 ++++ b/utf8.h 2023-02-21 12:00:40.186991497 +0100 +@@ -5,7 +5,7 @@ + + size_t display_mode_esc_sequence_len(const char *s); + int utf8_width(const char **start, size_t *remainder_p); +-int utf8_strnwidth(const char *string, int len, int skip_ansi); ++int utf8_strnwidth(const char *string, size_t len, int skip_ansi); + int utf8_strwidth(const char *string); + int is_utf8(const char *text); + int is_encoding_utf8(const char *name); diff --git a/SPECS/git.spec b/SPECS/git.spec index 25dc2b2..7245bac 100644 --- a/SPECS/git.spec +++ b/SPECS/git.spec @@ -23,9 +23,13 @@ %global with_desktop_vendor_tag 0 %endif +# https://fedoraproject.org/wiki/Changes/PythonMacroError +%define __python /usr/bin/python + + Name: git Version: 1.8.3.1 -Release: 23%{?dist} +Release: 24%{?dist} Summary: Fast Version Control System License: GPLv2 Group: Development/Tools @@ -89,6 +93,30 @@ Patch24: git-cve-2020-5260.patch # On top of that, there are included commit 9566231, c716fe4 and 07259e7, # which provides necessary plumbing. Patch25: git-2.17.15-CVE-2020-11008.patch +# Fix CVE-2022-41903: Heap overflow in `git archive`, `git log --format` +# leading to RCE +# https://bugzilla.redhat.com/show_bug.cgi?id=2162056 +# The fix includes all pretty and utf8 patches from Dec 9,2022: +# https://github.com/git/git/compare/v2.30.6...v2.30.7 +Patch26: git-cve-2022-41903.patch +# Fix CVE-2022-23521: gitattributes parsing integer overflow +# https://bugzilla.redhat.com/show_bug.cgi?id=2162055 +# The fix includes all attr patches from Dec 5,2022: +# https://github.com/git/git/compare/v2.30.6...v2.30.7 +# Some of these patches heavily differ from upstream as there has been +# a lot of changes to attribute handling in past years. For example there +# is no internal hashmap implementation, so the patches are adapted to the old one. +# It also includes: +# For plumbing: +# 428103c convert 'invalid_attr_name()' to 'attr_name_valid()' +# dde843e partially backported for skip_utf8_bom() +# fd1d672 + 58e4e51 partially backported for warning_errno() +# d616fbf to catch compiler error +# For tests: +# ca8d148 + 9eb2308 + e7884b3 all partially backported for test_must_be_empty() +# bd482d6 partially backported to support $SQ variable +# c4a7bce to not crash new tests in t0003 +Patch27: git-cve-2022-23521.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -346,6 +374,8 @@ Requires: gnome-keyring %patch23 -p1 %patch24 -p1 %patch25 -p1 +%patch26 -p1 +%patch27 -p1 chmod a+x t/t0011-hashmap.sh t/t1307-config-blob.sh t/t4139-apply-escape.sh t/t7415-submodule-names.sh t/t7416-submodule-dash-url.sh t/t7417-submodule-path-url.sh @@ -662,6 +692,10 @@ rm -rf %{buildroot} # No files for you! %changelog +* Tue Feb 21 2023 Ondřej Pohořelský - 1.8.3.1-24 +- Fixes CVE-2022-23521 and CVE-2022-41903 +- Resolves: #2162067 + * Mon May 25 2020 Vít Ondruch - 1.8.3.1-23 - Prevent crafted URL containing new lines, empty host or lacks a scheme to cause credential leak.