Merge pull request #4572 from pks-t/pks/index-secfixes Security fixes for reading index v4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e67497e..9f12016 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+v0.26.2
+-------
+
+This is a security release fixing memory handling issues when reading crafted
+repository index files. The issues allow for possible denial of service due to
+allocation of large memory and out-of-bound reads.
+
+As the index is never transferred via the network, exploitation requires an
+attacker to have access to the local repository.
+
v0.26.1
---------
diff --git a/include/git2/version.h b/include/git2/version.h
index 375d606..edb1dd5 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -7,10 +7,10 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "0.26.1"
+#define LIBGIT2_VERSION "0.26.2"
#define LIBGIT2_VER_MAJOR 0
#define LIBGIT2_VER_MINOR 26
-#define LIBGIT2_VER_REVISION 1
+#define LIBGIT2_VER_REVISION 2
#define LIBGIT2_VER_PATCH 0
#define LIBGIT2_SOVERSION 26
diff --git a/src/index.c b/src/index.c
index c29e90f..3ce7b0f 100644
--- a/src/index.c
+++ b/src/index.c
@@ -2295,8 +2295,9 @@ static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flag
}
}
-static size_t read_entry(
+static int read_entry(
git_index_entry **out,
+ size_t *out_size,
git_index *index,
const void *buffer,
size_t buffer_size,
@@ -2310,7 +2311,7 @@ static size_t read_entry(
char *tmp_path = NULL;
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
- return 0;
+ return -1;
/* buffer is not guaranteed to be aligned */
memcpy(&source, buffer, sizeof(struct entry_short));
@@ -2352,7 +2353,7 @@ static size_t read_entry(
path_end = memchr(path_ptr, '\0', buffer_size);
if (path_end == NULL)
- return 0;
+ return -1;
path_length = path_end - path_ptr;
}
@@ -2360,19 +2361,24 @@ static size_t read_entry(
entry_size = index_entry_size(path_length, 0, entry.flags);
entry.path = (char *)path_ptr;
} else {
- size_t varint_len;
- size_t strip_len = git_decode_varint((const unsigned char *)path_ptr,
- &varint_len);
- size_t last_len = strlen(last);
- size_t prefix_len = last_len - strip_len;
- size_t suffix_len = strlen(path_ptr + varint_len);
- size_t path_len;
-
- if (varint_len == 0)
+ size_t varint_len, last_len, prefix_len, suffix_len, path_len;
+ uintmax_t strip_len;
+
+ strip_len = git_decode_varint((const unsigned char *)path_ptr, &varint_len);
+ last_len = strlen(last);
+
+ if (varint_len == 0 || last_len < strip_len)
return index_error_invalid("incorrect prefix length");
+ prefix_len = last_len - strip_len;
+ suffix_len = strlen(path_ptr + varint_len);
+
GITERR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len);
GITERR_CHECK_ALLOC_ADD(&path_len, path_len, 1);
+
+ if (path_len > GIT_PATH_MAX)
+ return index_error_invalid("unreasonable path length");
+
tmp_path = git__malloc(path_len);
GITERR_CHECK_ALLOC(tmp_path);
@@ -2382,16 +2388,20 @@ static size_t read_entry(
entry.path = tmp_path;
}
+ if (entry_size == 0)
+ return -1;
+
if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
- return 0;
+ return -1;
if (index_entry_dup(out, index, &entry) < 0) {
git__free(tmp_path);
- return 0;
+ return -1;
}
git__free(tmp_path);
- return entry_size;
+ *out_size = entry_size;
+ return 0;
}
static int read_header(struct index_header *dest, const void *buffer)
@@ -2495,10 +2505,9 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
/* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
git_index_entry *entry;
- size_t entry_size = read_entry(&entry, index, buffer, buffer_size, last);
+ size_t entry_size;
- /* 0 bytes read means an object corruption */
- if (entry_size == 0) {
+ if ((error = read_entry(&entry, &entry_size, index, buffer, buffer_size, last)) < 0) {
error = index_error_invalid("invalid entry");
goto done;
}