Skip to content

Commit 090fd4f

Browse files
peffgitster
authored andcommitted
upload-archive: use argv_array to store client arguments
The current parsing scheme for upload-archive is to pack arguments into a fixed-size buffer, separated by NULs, and put a pointer to each argument in the buffer into a fixed-size argv array. This works fine, and the limits are high enough that nobody reasonable is going to hit them, but it makes the code hard to follow. Instead, let's just stuff the arguments into an argv_array, which is much simpler. That lifts the "all arguments must fit inside 4K together" limit. We could also trivially lift the MAX_ARGS limitation (in fact, we have to keep extra code to enforce it). But that would mean a client could force us to allocate an arbitrary amount of memory simply by sending us "argument" lines. By limiting the MAX_ARGS, we limit an attacker to about 4 megabytes (64 times a maximum 64K packet buffer). That may sound like a lot compared to the 4K limit, but it's not a big deal compared to what git-archive will actually allocate while working (e.g., to load blobs into memory). The important thing is that it is bounded. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 6379dd0 commit 090fd4f

File tree

1 file changed

+14
-21
lines changed

1 file changed

+14
-21
lines changed

builtin/upload-archive.c

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "pkt-line.h"
88
#include "sideband.h"
99
#include "run-command.h"
10+
#include "argv-array.h"
1011

1112
static const char upload_archive_usage[] =
1213
"git upload-archive <repo>";
@@ -18,10 +19,9 @@ static const char deadchild[] =
1819

1920
int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
2021
{
21-
const char *sent_argv[MAX_ARGS];
22+
struct argv_array sent_argv = ARGV_ARRAY_INIT;
2223
const char *arg_cmd = "argument ";
23-
char *p, buf[4096];
24-
int sent_argc;
24+
char buf[4096];
2525
int len;
2626

2727
if (argc != 2)
@@ -31,33 +31,26 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
3131
die("'%s' does not appear to be a git repository", argv[1]);
3232

3333
/* put received options in sent_argv[] */
34-
sent_argc = 1;
35-
sent_argv[0] = "git-upload-archive";
36-
for (p = buf;;) {
34+
argv_array_push(&sent_argv, "git-upload-archive");
35+
for (;;) {
3736
/* This will die if not enough free space in buf */
38-
len = packet_read_line(0, p, (buf + sizeof buf) - p);
37+
len = packet_read_line(0, buf, sizeof(buf));
3938
if (len == 0)
4039
break; /* got a flush */
41-
if (sent_argc > MAX_ARGS - 2)
42-
die("Too many options (>%d)", MAX_ARGS - 2);
40+
if (sent_argv.argc > MAX_ARGS)
41+
die("Too many options (>%d)", MAX_ARGS - 1);
4342

44-
if (p[len-1] == '\n') {
45-
p[--len] = 0;
43+
if (buf[len-1] == '\n') {
44+
buf[--len] = 0;
4645
}
47-
if (len < strlen(arg_cmd) ||
48-
strncmp(arg_cmd, p, strlen(arg_cmd)))
49-
die("'argument' token or flush expected");
5046

51-
len -= strlen(arg_cmd);
52-
memmove(p, p + strlen(arg_cmd), len);
53-
sent_argv[sent_argc++] = p;
54-
p += len;
55-
*p++ = 0;
47+
if (prefixcmp(buf, arg_cmd))
48+
die("'argument' token or flush expected");
49+
argv_array_push(&sent_argv, buf + strlen(arg_cmd));
5650
}
57-
sent_argv[sent_argc] = NULL;
5851

5952
/* parse all options sent by the client */
60-
return write_archive(sent_argc, sent_argv, prefix, 0, NULL, 1);
53+
return write_archive(sent_argv.argc, sent_argv.argv, prefix, 0, NULL, 1);
6154
}
6255

6356
__attribute__((format (printf, 1, 2)))

0 commit comments

Comments
 (0)