http-backend: respect CONTENT_LENGTH as specified by rfc3875

http-backend reads whole input until EOF. However, the RFC 3875 specifies that a script must read only as many bytes as specified by CONTENT_LENGTH environment variable. Web server may exercise the specification by not closing the script's standard input after writing content. In that case http-backend would hang waiting for the input. The issue is known to happen with IIS/Windows, for example. Make http-backend read only CONTENT_LENGTH bytes, if it's defined, rather than the whole input until EOF. If the variable is not defined, keep older behavior of reading until EOF because it is used to support chunked transfer-encoding. This commit only fixes buffered input, whcih reads whole body before processign it. Non-buffered input is going to be fixed in subsequent commit. Signed-off-by: Florian Manschwetus <manschwetus@cs-software-gmbh.de> [mk: fixed trivial build failures and polished style issues] Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Max Kirillov <max@max630.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Max Kirillov committed Jun 10, 2018 at 18:05 UTC c79edf73f4b018310428632887f9ce2ce32d839a
3 files changed +49 -8
config.c
+1 -1
@@ -921,7 +921,7 @@ int git_parse_ulong(const char *value, unsigned long *ret)
921 return 1;
922 }
923
924 -static int git_parse_ssize_t(const char *value, ssize_t *ret)
924 +int git_parse_ssize_t(const char *value, ssize_t *ret)
925 {
926 intmax_t tmp;
927 if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
config.h
+1
@@ -73,6 +73,7 @@ extern void git_config(config_fn_t fn, void *);
73 extern int config_with_options(config_fn_t fn, void *,
74 struct git_config_source *config_source,
75 const struct config_options *opts);
76 +extern int git_parse_ssize_t(const char *, ssize_t *);
77 extern int git_parse_ulong(const char *, unsigned long *);
78 extern int git_parse_maybe_bool(const char *);
79 extern int git_config_int(const char *, const char *);
http-backend.c
+47 -7
@@ -290,7 +290,7 @@ static void write_to_child(int out, const unsigned char *buf, ssize_t len, const
290 * hit max_request_buffer we die (we'd rather reject a
291 * maliciously large request than chew up infinite memory).
292 */
293 -static ssize_t read_request(int fd, unsigned char **out)
293 +static ssize_t read_request_eof(int fd, unsigned char **out)
294 {
295 size_t len = 0, alloc = 8192;
296 unsigned char *buf = xmalloc(alloc);
@@ -327,7 +327,46 @@ static ssize_t read_request(int fd, unsigned char **out)
327 }
328 }
329
330 -static void inflate_request(const char *prog_name, int out, int buffer_input)
330 +static ssize_t read_request_fixed_len(int fd, ssize_t req_len, unsigned char **out)
331 +{
332 + unsigned char *buf = NULL;
333 + ssize_t cnt = 0;
334 +
335 + if (max_request_buffer < req_len) {
336 + die("request was larger than our maximum size (%lu): "
337 + "%" PRIuMAX "; try setting GIT_HTTP_MAX_REQUEST_BUFFER",
338 + max_request_buffer, (uintmax_t)req_len);
339 + }
340 +
341 + buf = xmalloc(req_len);
342 + cnt = read_in_full(fd, buf, req_len);
343 + if (cnt < 0) {
344 + free(buf);
345 + return -1;
346 + }
347 + *out = buf;
348 + return cnt;
349 +}
350 +
351 +static ssize_t get_content_length(void)
352 +{
353 + ssize_t val = -1;
354 + const char *str = getenv("CONTENT_LENGTH");
355 +
356 + if (str && !git_parse_ssize_t(str, &val))
357 + die("failed to parse CONTENT_LENGTH: %s", str);
358 + return val;
359 +}
360 +
361 +static ssize_t read_request(int fd, unsigned char **out, ssize_t req_len)
362 +{
363 + if (req_len < 0)
364 + return read_request_eof(fd, out);
365 + else
366 + return read_request_fixed_len(fd, req_len, out);
367 +}
368 +
369 +static void inflate_request(const char *prog_name, int out, int buffer_input, ssize_t req_len)
370 {
371 git_zstream stream;
372 unsigned char *full_request = NULL;
@@ -345,7 +384,7 @@ static void inflate_request(const char *prog_name, int out, int buffer_input)
384 if (full_request)
385 n = 0; /* nothing left to read */
386 else
348 - n = read_request(0, &full_request);
387 + n = read_request(0, &full_request, req_len);
388 stream.next_in = full_request;
389 } else {
390 n = xread(0, in_buf, sizeof(in_buf));
@@ -381,10 +420,10 @@ done:
420 free(full_request);
421 }
422
384 -static void copy_request(const char *prog_name, int out)
423 +static void copy_request(const char *prog_name, int out, ssize_t req_len)
424 {
425 unsigned char *buf;
387 - ssize_t n = read_request(0, &buf);
426 + ssize_t n = read_request(0, &buf, req_len);
427 if (n < 0)
428 die_errno("error reading request body");
429 write_to_child(out, buf, n, prog_name);
@@ -399,6 +438,7 @@ static void run_service(const char **argv, int buffer_input)
438 const char *host = getenv("REMOTE_ADDR");
439 int gzipped_request = 0;
440 struct child_process cld = CHILD_PROCESS_INIT;
441 + ssize_t req_len = get_content_length();
442
443 if (encoding && !strcmp(encoding, "gzip"))
444 gzipped_request = 1;
@@ -425,9 +465,9 @@ static void run_service(const char **argv, int buffer_input)
465
466 close(1);
467 if (gzipped_request)
428 - inflate_request(argv[0], cld.in, buffer_input);
468 + inflate_request(argv[0], cld.in, buffer_input, req_len);
469 else if (buffer_input)
430 - copy_request(argv[0], cld.in);
470 + copy_request(argv[0], cld.in, req_len);
471 else
472 close(0);
473