summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff King <peff@peff.net>2014-06-19 21:30:26 (GMT)
committerJunio C Hamano <gitster@pobox.com>2014-06-19 22:20:56 (GMT)
commit45bc131dd3e1eb6edd903957cf9d42f37ad02181 (patch)
tree85c02d938724708082007c69351a1f938bdc6766
parentf33206992de994424036a7d9912a968ab9829e6e (diff)
downloadgit-45bc131dd3e1eb6edd903957cf9d42f37ad02181.zip
git-45bc131dd3e1eb6edd903957cf9d42f37ad02181.tar.gz
git-45bc131dd3e1eb6edd903957cf9d42f37ad02181.tar.bz2
unique_path: fix unlikely heap overflow
When merge-recursive creates a unique filename, it uses a template like: path~branch_%d where the final "_%d" is filled by an incrementing counter until we find a unique name. We allocate 8 characters for the counter, but there is no logic to limit the size of the integer. Of course, this is extremely unlikely, as you would need a hundred million collisions to trigger the problem. Even if an attacker constructed a specialized repo, it is unlikely that the victim would have the patience to run the merge. However, we can make it trivially correct (and hopefully more readable) by using a strbuf. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--merge-recursive.c41
1 files changed, 26 insertions, 15 deletions
diff --git a/merge-recursive.c b/merge-recursive.c
index 532a1da..398a734 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -601,25 +601,36 @@ static int remove_file(struct merge_options *o, int clean,
return 0;
}
+/* add a string to a strbuf, but converting "/" to "_" */
+static void add_flattened_path(struct strbuf *out, const char *s)
+{
+ size_t i = out->len;
+ strbuf_addstr(out, s);
+ for (; i < out->len; i++)
+ if (out->buf[i] == '/')
+ out->buf[i] = '_';
+}
+
static char *unique_path(struct merge_options *o, const char *path, const char *branch)
{
- char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+ struct strbuf newpath = STRBUF_INIT;
int suffix = 0;
struct stat st;
- char *p = newpath + strlen(path);
- strcpy(newpath, path);
- *(p++) = '~';
- strcpy(p, branch);
- for (; *p; ++p)
- if ('/' == *p)
- *p = '_';
- while (string_list_has_string(&o->current_file_set, newpath) ||
- string_list_has_string(&o->current_directory_set, newpath) ||
- lstat(newpath, &st) == 0)
- sprintf(p, "_%d", suffix++);
-
- string_list_insert(&o->current_file_set, newpath);
- return newpath;
+ size_t base_len;
+
+ strbuf_addf(&newpath, "%s~", path);
+ add_flattened_path(&newpath, branch);
+
+ base_len = newpath.len;
+ while (string_list_has_string(&o->current_file_set, newpath.buf) ||
+ string_list_has_string(&o->current_directory_set, newpath.buf) ||
+ lstat(newpath.buf, &st) == 0) {
+ strbuf_setlen(&newpath, base_len);
+ strbuf_addf(&newpath, "_%d", suffix++);
+ }
+
+ string_list_insert(&o->current_file_set, newpath.buf);
+ return strbuf_detach(&newpath, NULL);
}
static int dir_in_way(const char *path, int check_working_copy)