@@ -13,18 +13,20 @@ static const char content_type[] = "Content-Type";
1313static const char content_length [] = "Content-Length" ;
1414static const char last_modified [] = "Last-Modified" ;
1515static int getanyfile = 1 ;
16+ static unsigned long max_request_buffer = 10 * 1024 * 1024 ;
1617
1718static struct string_list * query_params ;
1819
1920struct rpc_service {
2021 const char * name ;
2122 const char * config_name ;
23+ unsigned buffer_input : 1 ;
2224 signed enabled : 2 ;
2325};
2426
2527static struct rpc_service rpc_service [] = {
26- { "upload-pack" , "uploadpack" , 1 },
27- { "receive-pack" , "receivepack" , -1 },
28+ { "upload-pack" , "uploadpack" , 1 , 1 },
29+ { "receive-pack" , "receivepack" , 0 , -1 },
2830};
2931
3032static struct string_list * get_parameters (void )
@@ -225,6 +227,7 @@ static void http_config(void)
225227 struct strbuf var = STRBUF_INIT ;
226228
227229 git_config_get_bool ("http.getanyfile" , & getanyfile );
230+ git_config_get_ulong ("http.maxrequestbuffer" , & max_request_buffer );
228231
229232 for (i = 0 ; i < ARRAY_SIZE (rpc_service ); i ++ ) {
230233 struct rpc_service * svc = & rpc_service [i ];
@@ -266,9 +269,52 @@ static struct rpc_service *select_service(const char *name)
266269 return svc ;
267270}
268271
269- static void inflate_request (const char * prog_name , int out )
272+ /*
273+ * This is basically strbuf_read(), except that if we
274+ * hit max_request_buffer we die (we'd rather reject a
275+ * maliciously large request than chew up infinite memory).
276+ */
277+ static ssize_t read_request (int fd , unsigned char * * out )
278+ {
279+ size_t len = 0 , alloc = 8192 ;
280+ unsigned char * buf = xmalloc (alloc );
281+
282+ if (max_request_buffer < alloc )
283+ max_request_buffer = alloc ;
284+
285+ while (1 ) {
286+ ssize_t cnt ;
287+
288+ cnt = read_in_full (fd , buf + len , alloc - len );
289+ if (cnt < 0 ) {
290+ free (buf );
291+ return -1 ;
292+ }
293+
294+ /* partial read from read_in_full means we hit EOF */
295+ len += cnt ;
296+ if (len < alloc ) {
297+ * out = buf ;
298+ return len ;
299+ }
300+
301+ /* otherwise, grow and try again (if we can) */
302+ if (alloc == max_request_buffer )
303+ die ("request was larger than our maximum size (%lu);"
304+ " try setting GIT_HTTP_MAX_REQUEST_BUFFER" ,
305+ max_request_buffer );
306+
307+ alloc = alloc_nr (alloc );
308+ if (alloc > max_request_buffer )
309+ alloc = max_request_buffer ;
310+ REALLOC_ARRAY (buf , alloc );
311+ }
312+ }
313+
314+ static void inflate_request (const char * prog_name , int out , int buffer_input )
270315{
271316 git_zstream stream ;
317+ unsigned char * full_request = NULL ;
272318 unsigned char in_buf [8192 ];
273319 unsigned char out_buf [8192 ];
274320 unsigned long cnt = 0 ;
@@ -277,11 +323,21 @@ static void inflate_request(const char *prog_name, int out)
277323 git_inflate_init_gzip_only (& stream );
278324
279325 while (1 ) {
280- ssize_t n = xread (0 , in_buf , sizeof (in_buf ));
326+ ssize_t n ;
327+
328+ if (buffer_input ) {
329+ if (full_request )
330+ n = 0 ; /* nothing left to read */
331+ else
332+ n = read_request (0 , & full_request );
333+ stream .next_in = full_request ;
334+ } else {
335+ n = xread (0 , in_buf , sizeof (in_buf ));
336+ stream .next_in = in_buf ;
337+ }
338+
281339 if (n <= 0 )
282340 die ("request ended in the middle of the gzip stream" );
283-
284- stream .next_in = in_buf ;
285341 stream .avail_in = n ;
286342
287343 while (0 < stream .avail_in ) {
@@ -307,9 +363,22 @@ static void inflate_request(const char *prog_name, int out)
307363done :
308364 git_inflate_end (& stream );
309365 close (out );
366+ free (full_request );
367+ }
368+
369+ static void copy_request (const char * prog_name , int out )
370+ {
371+ unsigned char * buf ;
372+ ssize_t n = read_request (0 , & buf );
373+ if (n < 0 )
374+ die_errno ("error reading request body" );
375+ if (write_in_full (out , buf , n ) != n )
376+ die ("%s aborted reading request" , prog_name );
377+ close (out );
378+ free (buf );
310379}
311380
312- static void run_service (const char * * argv )
381+ static void run_service (const char * * argv , int buffer_input )
313382{
314383 const char * encoding = getenv ("HTTP_CONTENT_ENCODING" );
315384 const char * user = getenv ("REMOTE_USER" );
@@ -334,15 +403,17 @@ static void run_service(const char **argv)
334403 "GIT_COMMITTER_EMAIL=%s@http.%s" , user , host );
335404
336405 cld .argv = argv ;
337- if (gzipped_request )
406+ if (buffer_input || gzipped_request )
338407 cld .in = -1 ;
339408 cld .git_cmd = 1 ;
340409 if (start_command (& cld ))
341410 exit (1 );
342411
343412 close (1 );
344413 if (gzipped_request )
345- inflate_request (argv [0 ], cld .in );
414+ inflate_request (argv [0 ], cld .in , buffer_input );
415+ else if (buffer_input )
416+ copy_request (argv [0 ], cld .in );
346417 else
347418 close (0 );
348419
@@ -392,7 +463,7 @@ static void get_info_refs(char *arg)
392463 packet_flush (1 );
393464
394465 argv [0 ] = svc -> name ;
395- run_service (argv );
466+ run_service (argv , 0 );
396467
397468 } else {
398469 select_getanyfile ();
@@ -496,25 +567,28 @@ static void service_rpc(char *service_name)
496567 end_headers ();
497568
498569 argv [0 ] = svc -> name ;
499- run_service (argv );
570+ run_service (argv , svc -> buffer_input );
500571 strbuf_release (& buf );
501572}
502573
574+ static int dead ;
503575static NORETURN void die_webcgi (const char * err , va_list params )
504576{
505- static int dead ;
577+ if (dead <= 1 ) {
578+ vreportf ("fatal: " , err , params );
506579
507- if (!dead ) {
508- dead = 1 ;
509580 http_status (500 , "Internal Server Error" );
510581 hdr_nocache ();
511582 end_headers ();
512-
513- vreportf ("fatal: " , err , params );
514583 }
515584 exit (0 ); /* we successfully reported a failure ;-) */
516585}
517586
587+ static int die_webcgi_recursing (void )
588+ {
589+ return dead ++ > 1 ;
590+ }
591+
518592static char * getdir (void )
519593{
520594 struct strbuf buf = STRBUF_INIT ;
@@ -569,6 +643,7 @@ int main(int argc, char **argv)
569643
570644 git_extract_argv0_path (argv [0 ]);
571645 set_die_routine (die_webcgi );
646+ set_die_is_recursing_routine (die_webcgi_recursing );
572647
573648 if (!method )
574649 die ("No REQUEST_METHOD from server" );
@@ -619,6 +694,9 @@ int main(int argc, char **argv)
619694 not_found ("Repository not exported: '%s'" , dir );
620695
621696 http_config ();
697+ max_request_buffer = git_env_ulong ("GIT_HTTP_MAX_REQUEST_BUFFER" ,
698+ max_request_buffer );
699+
622700 cmd -> imp (cmd_arg );
623701 return 0 ;
624702}
0 commit comments