Skip to content

Commit c79edf7

Browse files
max630gitster
authored andcommitted
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 <[email protected]> [mk: fixed trivial build failures and polished style issues] Helped-by: Junio C Hamano <[email protected]> Signed-off-by: Max Kirillov <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6b1fae1 commit c79edf7

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

config.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ int git_parse_ulong(const char *value, unsigned long *ret)
921921
return 1;
922922
}
923923

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)
925925
{
926926
intmax_t tmp;
927927
if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))

config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ extern void git_config(config_fn_t fn, void *);
7373
extern int config_with_options(config_fn_t fn, void *,
7474
struct git_config_source *config_source,
7575
const struct config_options *opts);
76+
extern int git_parse_ssize_t(const char *, ssize_t *);
7677
extern int git_parse_ulong(const char *, unsigned long *);
7778
extern int git_parse_maybe_bool(const char *);
7879
extern int git_config_int(const char *, const char *);

http-backend.c

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ static void write_to_child(int out, const unsigned char *buf, ssize_t len, const
290290
* hit max_request_buffer we die (we'd rather reject a
291291
* maliciously large request than chew up infinite memory).
292292
*/
293-
static ssize_t read_request(int fd, unsigned char **out)
293+
static ssize_t read_request_eof(int fd, unsigned char **out)
294294
{
295295
size_t len = 0, alloc = 8192;
296296
unsigned char *buf = xmalloc(alloc);
@@ -327,7 +327,46 @@ static ssize_t read_request(int fd, unsigned char **out)
327327
}
328328
}
329329

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)
331370
{
332371
git_zstream stream;
333372
unsigned char *full_request = NULL;
@@ -345,7 +384,7 @@ static void inflate_request(const char *prog_name, int out, int buffer_input)
345384
if (full_request)
346385
n = 0; /* nothing left to read */
347386
else
348-
n = read_request(0, &full_request);
387+
n = read_request(0, &full_request, req_len);
349388
stream.next_in = full_request;
350389
} else {
351390
n = xread(0, in_buf, sizeof(in_buf));
@@ -381,10 +420,10 @@ static void inflate_request(const char *prog_name, int out, int buffer_input)
381420
free(full_request);
382421
}
383422

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)
385424
{
386425
unsigned char *buf;
387-
ssize_t n = read_request(0, &buf);
426+
ssize_t n = read_request(0, &buf, req_len);
388427
if (n < 0)
389428
die_errno("error reading request body");
390429
write_to_child(out, buf, n, prog_name);
@@ -399,6 +438,7 @@ static void run_service(const char **argv, int buffer_input)
399438
const char *host = getenv("REMOTE_ADDR");
400439
int gzipped_request = 0;
401440
struct child_process cld = CHILD_PROCESS_INIT;
441+
ssize_t req_len = get_content_length();
402442

403443
if (encoding && !strcmp(encoding, "gzip"))
404444
gzipped_request = 1;
@@ -425,9 +465,9 @@ static void run_service(const char **argv, int buffer_input)
425465

426466
close(1);
427467
if (gzipped_request)
428-
inflate_request(argv[0], cld.in, buffer_input);
468+
inflate_request(argv[0], cld.in, buffer_input, req_len);
429469
else if (buffer_input)
430-
copy_request(argv[0], cld.in);
470+
copy_request(argv[0], cld.in, req_len);
431471
else
432472
close(0);
433473

0 commit comments

Comments
 (0)