summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2018-03-31 22:04:17 -0700
committerJunio C Hamano <gitster@pobox.com>2018-03-31 22:04:17 -0700
commit4ec289f1bfcebde6eae963faaa9a787c6577c2ff (patch)
treecaff0c18470ee8b37f24453b37c60aa945b93359
parente4bb7d09fc69cbffd674f9d308b45eca11ebb6e0 (diff)
parent6cd4180bc97f5b54ad88c69d3eef1dfe9199fbca (diff)
downloadgit-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--Makefile1
-rw-r--r--config.c2
-rw-r--r--config.h1
-rw-r--r--http-backend.c49
-rw-r--r--t/helper/test-print-larger-than-ssize.c10
-rwxr-xr-xt/t5560-http-backend-noserver.sh30
6 files changed, 91 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index fde5e196a7..4571b3080c 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/config.c b/config.c
index fc16c0b855..5835b77691 100644
--- a/config.c
+++ b/config.c
@@ -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)))
diff --git a/config.h b/config.h
index 0e060779d9..08b60ee383 100644
--- a/config.h
+++ b/config.h
@@ -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