diff options
author | Junio C Hamano <gitster@pobox.com> | 2018-03-31 22:04:17 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2018-03-31 22:04:17 -0700 |
commit | 4ec289f1bfcebde6eae963faaa9a787c6577c2ff (patch) | |
tree | caff0c18470ee8b37f24453b37c60aa945b93359 | |
parent | e4bb7d09fc69cbffd674f9d308b45eca11ebb6e0 (diff) | |
parent | 6cd4180bc97f5b54ad88c69d3eef1dfe9199fbca (diff) | |
download | git-4ec289f1bfcebde6eae963faaa9a787c6577c2ff.tar.gz |
Merge branch 'mk/http-backend-content-length' into pu
The http-backend (used for smart-http transport) used to slurp the
whole input until EOF, without paying attention to CONTENT_LENGTH
that is supplied in the environment and instead expecting the Web
server to close the input stream. This has been fixed.
* mk/http-backend-content-length:
SQUASH???
t5560-http-backend-noserver.sh: add CONTENT_LENGTH cases
SQUASH???
http-backend: respect CONTENT_LENGTH as specified by rfc3875
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | config.c | 2 | ||||
-rw-r--r-- | config.h | 1 | ||||
-rw-r--r-- | http-backend.c | 49 | ||||
-rw-r--r-- | t/helper/test-print-larger-than-ssize.c | 10 | ||||
-rwxr-xr-x | t/t5560-http-backend-noserver.sh | 30 |
6 files changed, 91 insertions, 2 deletions
@@ -726,6 +726,7 @@ TEST_PROGRAMS_NEED_X += test-json-writer TEST_PROGRAMS_NEED_X += test-line-buffer TEST_PROGRAMS_NEED_X += test-parse-options TEST_PROGRAMS_NEED_X += test-pkt-line +TEST_PROGRAMS_NEED_X += test-print-larger-than-ssize TEST_PROGRAMS_NEED_X += test-svn-fe TEST_PROGRAMS_NEED_X += test-tool @@ -854,7 +854,7 @@ int git_parse_ulong(const char *value, unsigned long *ret) return 1; } -static int git_parse_ssize_t(const char *value, ssize_t *ret) +int git_parse_ssize_t(const char *value, ssize_t *ret) { intmax_t tmp; if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t))) @@ -48,6 +48,7 @@ extern void git_config(config_fn_t fn, void *); extern int config_with_options(config_fn_t fn, void *, struct git_config_source *config_source, const struct config_options *opts); +extern int git_parse_ssize_t(const char *, ssize_t *); extern int git_parse_ulong(const char *, unsigned long *); extern int git_parse_maybe_bool(const char *); extern int git_config_int(const char *, const char *); diff --git a/http-backend.c b/http-backend.c index bf9b9199e1..af7309a9fa 100644 --- a/http-backend.c +++ b/http-backend.c @@ -284,7 +284,7 @@ static struct rpc_service *select_service(struct strbuf *hdr, const char *name) * hit max_request_buffer we die (we'd rather reject a * maliciously large request than chew up infinite memory). */ -static ssize_t read_request(int fd, unsigned char **out) +static ssize_t read_request_eof(int fd, unsigned char **out) { size_t len = 0, alloc = 8192; unsigned char *buf = xmalloc(alloc); @@ -321,6 +321,53 @@ static ssize_t read_request(int fd, unsigned char **out) } } +static ssize_t read_request_fixed_len(int fd, ssize_t req_len, unsigned char **out) +{ + unsigned char *buf = NULL; + ssize_t cnt = 0; + + if (max_request_buffer < req_len) { + die("request was larger than our maximum size (%lu): " + "%" PRIuMAX "; try setting GIT_HTTP_MAX_REQUEST_BUFFER", + max_request_buffer, (uintmax_t)req_len); + } + + if (req_len <= 0) { + *out = NULL; + return 0; + } + + buf = xmalloc(req_len); + cnt = read_in_full(fd, buf, req_len); + if (cnt < 0) { + free(buf); + return -1; + } else { + *out = buf; + return cnt; + } +} + +static ssize_t env_content_length(void) +{ + ssize_t val = -1; + const char *str = getenv("CONTENT_LENGTH"); + + if (str && !git_parse_ssize_t(str, &val)) + die("failed to parse CONTENT_LENGTH: %s", str); + return val; +} + +static ssize_t read_request(int fd, unsigned char **out) +{ + ssize_t req_len = env_content_length(); + + if (req_len < 0) + return read_request_eof(fd, out); + else + return read_request_fixed_len(fd, req_len, out); +} + static void inflate_request(const char *prog_name, int out, int buffer_input) { git_zstream stream; diff --git a/t/helper/test-print-larger-than-ssize.c b/t/helper/test-print-larger-than-ssize.c new file mode 100644 index 0000000000..b9852c493d --- /dev/null +++ b/t/helper/test-print-larger-than-ssize.c @@ -0,0 +1,10 @@ +#include "cache.h" + +int cmd_main(int argc, const char **argv) +{ + size_t large = ~0; + + large = ~(large & ~(large >> 1)) + 1; + printf("%" PRIuMAX "\n", (uintmax_t) large); + return 0; +} diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh index 9fafcf1945..112b5d6eb2 100755 --- a/t/t5560-http-backend-noserver.sh +++ b/t/t5560-http-backend-noserver.sh @@ -71,4 +71,34 @@ test_expect_success 'http-backend blocks bad PATH_INFO' ' expect_aliased 1 //domain/data.txt ' +# overrides existing definition for further cases +run_backend () { + CONTENT_LENGTH="${#2}" && export CONTENT_LENGTH && + ( echo "$2" && cat /dev/zero ) | + QUERY_STRING="${1#*[?]}" \ + PATH_TRANSLATED="$HTTPD_DOCUMENT_ROOT_PATH/${1%%[?]*}" \ + git http-backend >act.out 2>act.err +} + +test_expect_success 'CONTENT_LENGTH set and infinite input' ' + config http.uploadpack true && + GET info/refs?service=git-upload-pack "200 OK" && + ! grep "fatal:.*" act.err && + POST git-upload-pack 0000 "200 OK" && + ! grep "fatal:.*" act.err +' + +test_expect_success 'CONTENT_LENGTH overflow ssite_t' ' + NOT_FIT_IN_SSIZE=$("$GIT_BUILD_DIR/t/helper/test-print-larger-than-ssize") && + env \ + CONTENT_TYPE=application/x-git-upload-pack-request \ + QUERY_STRING=/repo.git/git-upload-pack \ + PATH_TRANSLATED="$PWD"/.git/git-upload-pack \ + GIT_HTTP_EXPORT_ALL=TRUE \ + REQUEST_METHOD=POST \ + CONTENT_LENGTH="$NOT_FIT_IN_SSIZE" \ + git http-backend </dev/zero >/dev/null 2>err && + grep -q "fatal:.*CONTENT_LENGTH" err +' + test_done |