From bb3788cebb814aa003941abcf484da872aa61412 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:05:40 -0400 Subject: add git_path_buf helper function If you have a function that uses git_path a lot, but would prefer to avoid the static buffers, it's useful to keep a single scratch buffer locally and reuse it for each call. You used to be able to do this with git_snpath: char buf[PATH_MAX]; foo(git_snpath(buf, sizeof(buf), "foo")); bar(git_snpath(buf, sizeof(buf), "bar")); but since 1a83c24, git_snpath has been replaced with strbuf_git_path. This is good, because it removes the arbitrary PATH_MAX limit. But using strbuf_git_path is more awkward for two reasons: 1. It adds to the buffer, rather than replacing it. This is consistent with other strbuf functions, but makes reuse of a single buffer more tedious. 2. It doesn't return the buffer, so you can't format as part of a function's arguments. The new git_path_buf solves both of these, so you can use it like: struct strbuf buf = STRBUF_INIT; foo(git_path_buf(&buf, "foo")); bar(git_path_buf(&buf, "bar")); strbuf_release(&buf); Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- path.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'path.c') diff --git a/path.c b/path.c index 95acbafa68..46a4d2714b 100644 --- a/path.c +++ b/path.c @@ -175,6 +175,16 @@ static void do_git_path(struct strbuf *buf, const char *fmt, va_list args) strbuf_cleanup_path(buf); } +char *git_path_buf(struct strbuf *buf, const char *fmt, ...) +{ + va_list args; + strbuf_reset(buf); + va_start(args, fmt); + do_git_path(buf, fmt, args); + va_end(args); + return buf->buf; +} + void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) { va_list args; -- cgit v1.2.1 From e9ba678175da28607d57043e1363c6252880dd7f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:07:45 -0400 Subject: enter_repo: convert fixed-size buffers to strbufs We use two PATH_MAX-sized buffers to represent the repo path, and must make sure not to overflow them. We do take care to check the lengths, but the logic is rather hard to follow, as we use several magic numbers (e.g., "PATH_MAX - 10"). And in fact you _can_ overflow the buffer if you have a ".git" file with an extremely long path in it. By switching to strbufs, these problems all go away. We do, however, retain the check that the initial input we get is no larger than PATH_MAX. This function is an entry point for untrusted repo names from the network, and it's a good idea to keep a sanity check (both to avoid allocating arbitrary amounts of memory, and also as a layer of defense against any downstream users of the names). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- path.c | 57 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'path.c') diff --git a/path.c b/path.c index 46a4d2714b..60e0390906 100644 --- a/path.c +++ b/path.c @@ -391,8 +391,8 @@ return_null: */ const char *enter_repo(const char *path, int strict) { - static char used_path[PATH_MAX]; - static char validated_path[PATH_MAX]; + static struct strbuf validated_path = STRBUF_INIT; + static struct strbuf used_path = STRBUF_INIT; if (!path) return NULL; @@ -407,46 +407,47 @@ const char *enter_repo(const char *path, int strict) while ((1 < len) && (path[len-1] == '/')) len--; + /* + * We can handle arbitrary-sized buffers, but this remains as a + * sanity check on untrusted input. + */ if (PATH_MAX <= len) return NULL; - strncpy(used_path, path, len); used_path[len] = 0 ; - strcpy(validated_path, used_path); - if (used_path[0] == '~') { - char *newpath = expand_user_path(used_path); - if (!newpath || (PATH_MAX - 10 < strlen(newpath))) { - free(newpath); + strbuf_reset(&used_path); + strbuf_reset(&validated_path); + strbuf_add(&used_path, path, len); + strbuf_add(&validated_path, path, len); + + if (used_path.buf[0] == '~') { + char *newpath = expand_user_path(used_path.buf); + if (!newpath) return NULL; - } - /* - * Copy back into the static buffer. A pity - * since newpath was not bounded, but other - * branches of the if are limited by PATH_MAX - * anyway. - */ - strcpy(used_path, newpath); free(newpath); + strbuf_attach(&used_path, newpath, strlen(newpath), + strlen(newpath)); } - else if (PATH_MAX - 10 < len) - return NULL; - len = strlen(used_path); for (i = 0; suffix[i]; i++) { struct stat st; - strcpy(used_path + len, suffix[i]); - if (!stat(used_path, &st) && + size_t baselen = used_path.len; + strbuf_addstr(&used_path, suffix[i]); + if (!stat(used_path.buf, &st) && (S_ISREG(st.st_mode) || - (S_ISDIR(st.st_mode) && is_git_directory(used_path)))) { - strcat(validated_path, suffix[i]); + (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) { + strbuf_addstr(&validated_path, suffix[i]); break; } + strbuf_setlen(&used_path, baselen); } if (!suffix[i]) return NULL; - gitfile = read_gitfile(used_path) ; - if (gitfile) - strcpy(used_path, gitfile); - if (chdir(used_path)) + gitfile = read_gitfile(used_path.buf) ; + if (gitfile) { + strbuf_reset(&used_path); + strbuf_addstr(&used_path, gitfile); + } + if (chdir(used_path.buf)) return NULL; - path = validated_path; + path = validated_path.buf; } else if (chdir(path)) return NULL; -- cgit v1.2.1 From 4635768809885bb1c063bc9f9eee38e413f85f0d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:07:47 -0400 Subject: remove_leading_path: use a strbuf for internal storage This function strcpy's directly into a PATH_MAX-sized buffer. There's only one caller, which feeds the git_dir into it, so it's not easy to trigger in practice (even if you fed a large $GIT_DIR through the environment or .git file, it would have to actually exist and be accessible on the filesystem to get to this point). We can fix it by moving to a strbuf. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- path.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'path.c') diff --git a/path.c b/path.c index 60e0390906..c597473e62 100644 --- a/path.c +++ b/path.c @@ -632,7 +632,7 @@ const char *relative_path(const char *in, const char *prefix, */ const char *remove_leading_path(const char *in, const char *prefix) { - static char buf[PATH_MAX + 1]; + static struct strbuf buf = STRBUF_INIT; int i = 0, j = 0; if (!prefix || !prefix[0]) @@ -661,11 +661,13 @@ const char *remove_leading_path(const char *in, const char *prefix) return in; while (is_dir_sep(in[j])) j++; + + strbuf_reset(&buf); if (!in[j]) - strcpy(buf, "."); + strbuf_addstr(&buf, "."); else - strcpy(buf, in + j); - return buf; + strbuf_addstr(&buf, in + j); + return buf.buf; } /* -- cgit v1.2.1 From 00b6c178c3ab475098f7a0bc63b2df2da508020c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:08:35 -0400 Subject: use strbuf_complete to conditionally append slash When working with paths in strbufs, we frequently want to ensure that a directory contains a trailing slash before appending to it. We can shorten this code (and make the intent more obvious) by calling strbuf_complete. Most of these cases are trivially identical conversions, but there are two things to note: - in a few cases we did not check that the strbuf is non-empty (which would lead to an out-of-bounds memory access). These were generally not triggerable in practice, either from earlier assertions, or typically because we would have just fed the strbuf to opendir(), which would choke on an empty path. - in a few cases we indexed the buffer with "original_len" or similar, rather than the current sb->len, and it is not immediately obvious from the diff that they are the same. In all of these cases, I manually verified that the strbuf does not change between the assignment and the strbuf_complete call. This does not convert cases which look like: if (sb->len && !is_dir_sep(sb->buf[sb->len - 1])) strbuf_addch(sb, '/'); as those are obviously semantically different. Some of these cases arguably should be doing that, but that is out of scope for this change, which aims purely for cleanup with no behavior change (and at least it will make such sites easier to find and examine in the future, as we can grep for strbuf_complete). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- path.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'path.c') diff --git a/path.c b/path.c index c597473e62..c105a9e083 100644 --- a/path.c +++ b/path.c @@ -240,8 +240,7 @@ static void do_submodule_path(struct strbuf *buf, const char *path, const char *git_dir; strbuf_addstr(buf, path); - if (buf->len && buf->buf[buf->len - 1] != '/') - strbuf_addch(buf, '/'); + strbuf_complete(buf, '/'); strbuf_addstr(buf, ".git"); git_dir = read_gitfile(buf->buf); -- cgit v1.2.1