From 5096d4909f9b13c7a650d9dbb7c9702ea7413566 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:06:08 -0400 Subject: convert trivial sprintf / strcpy calls to xsnprintf We sometimes sprintf into fixed-size buffers when we know that the buffer is large enough to fit the input (either because it's a constant, or because it's numeric input that is bounded in size). Likewise with strcpy of constant strings. However, these sites make it hard to audit sprintf and strcpy calls for buffer overflows, as a reader has to cross-reference the size of the array with the input. Let's use xsnprintf instead, which communicates to a reader that we don't expect this to overflow (and catches the mistake in case we do). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- http-push.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'http-push.c') diff --git a/http-push.c b/http-push.c index c98dad23df..154e67bb05 100644 --- a/http-push.c +++ b/http-push.c @@ -881,7 +881,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout) strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped); free(escaped); - sprintf(timeout_header, "Timeout: Second-%ld", timeout); + xsnprintf(timeout_header, sizeof(timeout_header), "Timeout: Second-%ld", timeout); dav_headers = curl_slist_append(dav_headers, timeout_header); dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); -- cgit v1.3 From ef1286d3c0ba714c6c2ae87e14edf3c462aef114 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:06:42 -0400 Subject: use xsnprintf for generating git object headers We generally use 32-byte buffers to format git's "type size" header fields. These should not generally overflow unless you can produce some truly gigantic objects (and our types come from our internal array of constant strings). But it is a good idea to use xsnprintf to make sure this is the case. Note that we slightly modify the interface to write_sha1_file_prepare, which nows uses "hdrlen" as an "in" parameter as well as an "out" (on the way in it stores the allocated size of the header, and on the way out it returns the ultimate size of the header). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/index-pack.c | 2 +- bulk-checkin.c | 4 ++-- fast-import.c | 4 ++-- http-push.c | 2 +- sha1_file.c | 13 +++++++------ 5 files changed, 13 insertions(+), 12 deletions(-) (limited to 'http-push.c') diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 3431de2362..1ad1bde696 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -441,7 +441,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size, int hdrlen; if (!is_delta_type(type)) { - hdrlen = sprintf(hdr, "%s %lu", typename(type), size) + 1; + hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 1; git_SHA1_Init(&c); git_SHA1_Update(&c, hdr, hdrlen); } else diff --git a/bulk-checkin.c b/bulk-checkin.c index 7cffc3a579..4347f5c76a 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -200,8 +200,8 @@ static int deflate_to_pack(struct bulk_checkin_state *state, if (seekback == (off_t) -1) return error("cannot find the current offset"); - header_len = sprintf((char *)obuf, "%s %" PRIuMAX, - typename(type), (uintmax_t)size) + 1; + header_len = xsnprintf((char *)obuf, sizeof(obuf), "%s %" PRIuMAX, + typename(type), (uintmax_t)size) + 1; git_SHA1_Init(&ctx); git_SHA1_Update(&ctx, obuf, header_len); diff --git a/fast-import.c b/fast-import.c index 6c7c3c9b66..d0c25024cd 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1035,8 +1035,8 @@ static int store_object( git_SHA_CTX c; git_zstream s; - hdrlen = sprintf((char *)hdr,"%s %lu", typename(type), - (unsigned long)dat->len) + 1; + hdrlen = xsnprintf((char *)hdr, sizeof(hdr), "%s %lu", + typename(type), (unsigned long)dat->len) + 1; git_SHA1_Init(&c); git_SHA1_Update(&c, hdr, hdrlen); git_SHA1_Update(&c, dat->buf, dat->len); diff --git a/http-push.c b/http-push.c index 154e67bb05..1f3788f634 100644 --- a/http-push.c +++ b/http-push.c @@ -361,7 +361,7 @@ static void start_put(struct transfer_request *request) git_zstream stream; unpacked = read_sha1_file(request->obj->sha1, &type, &len); - hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; + hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1; /* Set it up */ git_deflate_init(&stream, zlib_compression_level); diff --git a/sha1_file.c b/sha1_file.c index d295a3225a..f106091474 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1464,7 +1464,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map, return -1; /* Generate the header */ - hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1; + hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(obj_type), size) + 1; /* Sha1.. */ git_SHA1_Init(&c); @@ -2930,7 +2930,7 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len, git_SHA_CTX c; /* Generate the header */ - *hdrlen = sprintf(hdr, "%s %lu", type, len)+1; + *hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1; /* Sha1.. */ git_SHA1_Init(&c); @@ -2993,7 +2993,7 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1) { char hdr[32]; - int hdrlen; + int hdrlen = sizeof(hdr); write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); return 0; } @@ -3139,7 +3139,7 @@ static int freshen_packed_object(const unsigned char *sha1) int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1) { char hdr[32]; - int hdrlen; + int hdrlen = sizeof(hdr); /* Normally if we have it in the pack then we do not bother writing * it out into .git/objects/??/?{38} file. @@ -3157,7 +3157,8 @@ int hash_sha1_file_literally(const void *buf, unsigned long len, const char *typ int hdrlen, status = 0; /* type string, SP, %lu of the length plus NUL must fit this */ - header = xmalloc(strlen(type) + 32); + hdrlen = strlen(type) + 32; + header = xmalloc(hdrlen); write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen); if (!(flags & HASH_WRITE_OBJECT)) @@ -3185,7 +3186,7 @@ int force_object_loose(const unsigned char *sha1, time_t mtime) buf = read_packed_sha1(sha1, &type, &len); if (!buf) return error("cannot read sha1_file for %s", sha1_to_hex(sha1)); - hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; + hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1; ret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime); free(buf); -- cgit v1.3 From 0cc41428596ec1cd3862918ef781793ef7346ba5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:06:58 -0400 Subject: http-push: replace strcat with xsnprintf We account for these strcats in our initial allocation, but the code is confusing to follow and verify. Let's remember our original allocation length, and then xsnprintf can verify that we don't exceed it. Note that we can't just use xstrfmt here (which would be even cleaner) because the code tries to grow the buffer only when necessary. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- http-push.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'http-push.c') diff --git a/http-push.c b/http-push.c index 1f3788f634..37baff818f 100644 --- a/http-push.c +++ b/http-push.c @@ -786,21 +786,21 @@ xml_start_tag(void *userData, const char *name, const char **atts) { struct xml_ctx *ctx = (struct xml_ctx *)userData; const char *c = strchr(name, ':'); - int new_len; + int old_namelen, new_len; if (c == NULL) c = name; else c++; - new_len = strlen(ctx->name) + strlen(c) + 2; + old_namelen = strlen(ctx->name); + new_len = old_namelen + strlen(c) + 2; if (new_len > ctx->len) { ctx->name = xrealloc(ctx->name, new_len); ctx->len = new_len; } - strcat(ctx->name, "."); - strcat(ctx->name, c); + xsnprintf(ctx->name + old_namelen, ctx->len - old_namelen, ".%s", c); free(ctx->cdata); ctx->cdata = NULL; -- cgit v1.3 From 7d0581a9abe733d8880113370c4d956b50f5bd9f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:07:29 -0400 Subject: http-push: use strbuf instead of fwrite_buffer The http-push code defines an fwrite_buffer function for use as a curl callback; it just writes to a strbuf. There's no reason we need to use it ourselves, as we know we have a strbuf. This lets us format directly into it, rather than dealing with an extra temporary buffer (which required manual length computation). While we're here, let's also remove the literal tabs from the source in favor of "\t", which is more visually obvious. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- http-push.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'http-push.c') diff --git a/http-push.c b/http-push.c index 37baff818f..e501c282e6 100644 --- a/http-push.c +++ b/http-push.c @@ -1459,8 +1459,6 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; struct object *o; - int len; - char *ref_info; struct ref *ref; ref = alloc_ref(ls->dentry_name); @@ -1484,23 +1482,14 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls) return; } - len = strlen(ls->dentry_name) + 42; - ref_info = xcalloc(len + 1, 1); - sprintf(ref_info, "%s %s\n", - sha1_to_hex(ref->old_sha1), ls->dentry_name); - fwrite_buffer(ref_info, 1, len, buf); - free(ref_info); + strbuf_addf(buf, "%s\t%s\n", + sha1_to_hex(ref->old_sha1), ls->dentry_name); if (o->type == OBJ_TAG) { o = deref_tag(o, ls->dentry_name, 0); - if (o) { - len = strlen(ls->dentry_name) + 45; - ref_info = xcalloc(len + 1, 1); - sprintf(ref_info, "%s %s^{}\n", - sha1_to_hex(o->sha1), ls->dentry_name); - fwrite_buffer(ref_info, 1, len, buf); - free(ref_info); - } + if (o) + strbuf_addf(buf, "%s\t%s^{}\n", + sha1_to_hex(o->sha1), ls->dentry_name); } free(ref); } -- cgit v1.3 From a0355f6bcde6fb62d9629927b2e241353b38aeef Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:07:56 -0400 Subject: http-push: use an argv_array for setup_revisions This drops the magic number for the fixed-size argv arrays, so we do not have to wonder if we are overflowing it. We can also drop some confusing sha1_to_hex memory allocation (which seems to predate the ring of buffers allowing multiple calls), and get rid of an unchecked sprintf call. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- http-push.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) (limited to 'http-push.c') diff --git a/http-push.c b/http-push.c index e501c282e6..43a9036594 100644 --- a/http-push.c +++ b/http-push.c @@ -10,6 +10,7 @@ #include "remote.h" #include "list-objects.h" #include "sigchain.h" +#include "argv-array.h" #ifdef EXPAT_NEEDS_XMLPARSE_H #include @@ -1856,9 +1857,7 @@ int main(int argc, char **argv) new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { char old_hex[60], *new_hex; - const char *commit_argv[5]; - int commit_argc; - char *new_sha1_hex, *old_sha1_hex; + struct argv_array commit_argv = ARGV_ARRAY_INIT; if (!ref->peer_ref) continue; @@ -1937,27 +1936,15 @@ int main(int argc, char **argv) } /* Set up revision info for this refspec */ - commit_argc = 3; - new_sha1_hex = xstrdup(sha1_to_hex(ref->new_sha1)); - old_sha1_hex = NULL; - commit_argv[1] = "--objects"; - commit_argv[2] = new_sha1_hex; - if (!push_all && !is_null_sha1(ref->old_sha1)) { - old_sha1_hex = xmalloc(42); - sprintf(old_sha1_hex, "^%s", - sha1_to_hex(ref->old_sha1)); - commit_argv[3] = old_sha1_hex; - commit_argc++; - } - commit_argv[commit_argc] = NULL; + argv_array_push(&commit_argv, ""); /* ignored */ + argv_array_push(&commit_argv, "--objects"); + argv_array_push(&commit_argv, sha1_to_hex(ref->new_sha1)); + if (!push_all && !is_null_sha1(ref->old_sha1)) + argv_array_pushf(&commit_argv, "^%s", + sha1_to_hex(ref->old_sha1)); init_revisions(&revs, setup_git_directory()); - setup_revisions(commit_argc, commit_argv, &revs, NULL); + setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL); revs.edge_hint = 0; /* just in case */ - free(new_sha1_hex); - if (old_sha1_hex) { - free(old_sha1_hex); - commit_argv[1] = NULL; - } /* Generate a list of objects that need to be pushed */ pushing = 0; @@ -1986,6 +1973,7 @@ int main(int argc, char **argv) printf("%s %s\n", !rc ? "ok" : "error", ref->name); unlock_remote(ref_lock); check_locks(); + argv_array_clear(&commit_argv); } /* Update remote server info if appropriate */ -- cgit v1.3 From 2b87d3a89633fb6078203a77d2610175f94cef95 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 24 Sep 2015 17:08:05 -0400 Subject: drop strcpy in favor of raw sha1_to_hex In some cases where we strcpy() the result of sha1_to_hex(), there's no need; the result goes directly into a printf statement, and we can simply pass the return value from sha1_to_hex() directly. When this code was originally written, sha1_to_hex used a single buffer, and it was not safe to use it twice within a single expression. That changed as of dcb3450 (sha1_to_hex() usage cleanup, 2006-05-03), but this code was never updated. History-dug-by: Eric Sunshine Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- http-push.c | 6 ++---- walker.c | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'http-push.c') diff --git a/http-push.c b/http-push.c index 43a9036594..48f39b7f71 100644 --- a/http-push.c +++ b/http-push.c @@ -1856,7 +1856,6 @@ int main(int argc, char **argv) new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { - char old_hex[60], *new_hex; struct argv_array commit_argv = ARGV_ARRAY_INIT; if (!ref->peer_ref) @@ -1911,13 +1910,12 @@ int main(int argc, char **argv) } hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); new_refs++; - strcpy(old_hex, sha1_to_hex(ref->old_sha1)); - new_hex = sha1_to_hex(ref->new_sha1); fprintf(stderr, "updating '%s'", ref->name); if (strcmp(ref->name, ref->peer_ref->name)) fprintf(stderr, " using '%s'", ref->peer_ref->name); - fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex); + fprintf(stderr, "\n from %s\n to %s\n", + sha1_to_hex(ref->old_sha1), sha1_to_hex(ref->new_sha1)); if (dry_run) { if (helper_status) printf("ok %s\n", ref->name); diff --git a/walker.c b/walker.c index 44a936c1cf..cdeb63f319 100644 --- a/walker.c +++ b/walker.c @@ -17,10 +17,9 @@ void walker_say(struct walker *walker, const char *fmt, const char *hex) static void report_missing(const struct object *obj) { - char missing_hex[41]; - strcpy(missing_hex, sha1_to_hex(obj->sha1)); fprintf(stderr, "Cannot obtain needed %s %s\n", - obj->type ? typename(obj->type): "object", missing_hex); + obj->type ? typename(obj->type): "object", + sha1_to_hex(obj->sha1)); if (!is_null_sha1(current_commit_sha1)) fprintf(stderr, "while processing commit %s.\n", sha1_to_hex(current_commit_sha1)); -- cgit v1.3