@@ -13,18 +13,20 @@ static const char content_type[] = "Content-Type";
13
13
static const char content_length [] = "Content-Length" ;
14
14
static const char last_modified [] = "Last-Modified" ;
15
15
static int getanyfile = 1 ;
16
+ static unsigned long max_request_buffer = 10 * 1024 * 1024 ;
16
17
17
18
static struct string_list * query_params ;
18
19
19
20
struct rpc_service {
20
21
const char * name ;
21
22
const char * config_name ;
23
+ unsigned buffer_input : 1 ;
22
24
signed enabled : 2 ;
23
25
};
24
26
25
27
static 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 },
28
30
};
29
31
30
32
static struct string_list * get_parameters (void )
@@ -225,6 +227,7 @@ static void http_config(void)
225
227
struct strbuf var = STRBUF_INIT ;
226
228
227
229
git_config_get_bool ("http.getanyfile" , & getanyfile );
230
+ git_config_get_ulong ("http.maxrequestbuffer" , & max_request_buffer );
228
231
229
232
for (i = 0 ; i < ARRAY_SIZE (rpc_service ); i ++ ) {
230
233
struct rpc_service * svc = & rpc_service [i ];
@@ -266,9 +269,52 @@ static struct rpc_service *select_service(const char *name)
266
269
return svc ;
267
270
}
268
271
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 )
270
315
{
271
316
git_zstream stream ;
317
+ unsigned char * full_request = NULL ;
272
318
unsigned char in_buf [8192 ];
273
319
unsigned char out_buf [8192 ];
274
320
unsigned long cnt = 0 ;
@@ -277,11 +323,21 @@ static void inflate_request(const char *prog_name, int out)
277
323
git_inflate_init_gzip_only (& stream );
278
324
279
325
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
+
281
339
if (n <= 0 )
282
340
die ("request ended in the middle of the gzip stream" );
283
-
284
- stream .next_in = in_buf ;
285
341
stream .avail_in = n ;
286
342
287
343
while (0 < stream .avail_in ) {
@@ -307,9 +363,22 @@ static void inflate_request(const char *prog_name, int out)
307
363
done :
308
364
git_inflate_end (& stream );
309
365
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 );
310
379
}
311
380
312
- static void run_service (const char * * argv )
381
+ static void run_service (const char * * argv , int buffer_input )
313
382
{
314
383
const char * encoding = getenv ("HTTP_CONTENT_ENCODING" );
315
384
const char * user = getenv ("REMOTE_USER" );
@@ -334,15 +403,17 @@ static void run_service(const char **argv)
334
403
"GIT_COMMITTER_EMAIL=%s@http.%s" , user , host );
335
404
336
405
cld .argv = argv ;
337
- if (gzipped_request )
406
+ if (buffer_input || gzipped_request )
338
407
cld .in = -1 ;
339
408
cld .git_cmd = 1 ;
340
409
if (start_command (& cld ))
341
410
exit (1 );
342
411
343
412
close (1 );
344
413
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 );
346
417
else
347
418
close (0 );
348
419
@@ -392,7 +463,7 @@ static void get_info_refs(char *arg)
392
463
packet_flush (1 );
393
464
394
465
argv [0 ] = svc -> name ;
395
- run_service (argv );
466
+ run_service (argv , 0 );
396
467
397
468
} else {
398
469
select_getanyfile ();
@@ -496,25 +567,28 @@ static void service_rpc(char *service_name)
496
567
end_headers ();
497
568
498
569
argv [0 ] = svc -> name ;
499
- run_service (argv );
570
+ run_service (argv , svc -> buffer_input );
500
571
strbuf_release (& buf );
501
572
}
502
573
574
+ static int dead ;
503
575
static NORETURN void die_webcgi (const char * err , va_list params )
504
576
{
505
- static int dead ;
577
+ if (dead <= 1 ) {
578
+ vreportf ("fatal: " , err , params );
506
579
507
- if (!dead ) {
508
- dead = 1 ;
509
580
http_status (500 , "Internal Server Error" );
510
581
hdr_nocache ();
511
582
end_headers ();
512
-
513
- vreportf ("fatal: " , err , params );
514
583
}
515
584
exit (0 ); /* we successfully reported a failure ;-) */
516
585
}
517
586
587
+ static int die_webcgi_recursing (void )
588
+ {
589
+ return dead ++ > 1 ;
590
+ }
591
+
518
592
static char * getdir (void )
519
593
{
520
594
struct strbuf buf = STRBUF_INIT ;
@@ -569,6 +643,7 @@ int main(int argc, char **argv)
569
643
570
644
git_extract_argv0_path (argv [0 ]);
571
645
set_die_routine (die_webcgi );
646
+ set_die_is_recursing_routine (die_webcgi_recursing );
572
647
573
648
if (!method )
574
649
die ("No REQUEST_METHOD from server" );
@@ -619,6 +694,9 @@ int main(int argc, char **argv)
619
694
not_found ("Repository not exported: '%s'" , dir );
620
695
621
696
http_config ();
697
+ max_request_buffer = git_env_ulong ("GIT_HTTP_MAX_REQUEST_BUFFER" ,
698
+ max_request_buffer );
699
+
622
700
cmd -> imp (cmd_arg );
623
701
return 0 ;
624
702
}
0 commit comments