Merge branch 'cc/receive-pack-limit'
authorJunio C Hamano <gitster@pobox.com>
Fri, 9 Sep 2016 04:49:47 +0000 (21:49 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 9 Sep 2016 04:49:47 +0000 (21:49 -0700)
An incoming "git push" that attempts to push too many bytes can now
be rejected by setting a new configuration variable at the receiving
end.

* cc/receive-pack-limit:
receive-pack: allow a maximum input size to be specified
unpack-objects: add --max-input-size=<size> option
index-pack: add --max-input-size=<size> option

Documentation/config.txt
Documentation/git-index-pack.txt
Documentation/git-receive-pack.txt
Documentation/git-unpack-objects.txt
builtin/index-pack.c
builtin/receive-pack.c
builtin/unpack-objects.c
t/t5546-receive-limits.sh [new file with mode: 0755]
index 0bcb6790d6efbb2a812bc90b6f64e323cb3ec393..8a115b3702ebe6c9a8af376b978503c8db295064 100644 (file)
@@ -2517,6 +2517,12 @@ receive.unpackLimit::
        especially on slow filesystems.  If not set, the value of
        `transfer.unpackLimit` is used instead.
 
+receive.maxInputSize::
+       If the size of the incoming pack stream is larger than this
+       limit, then git-receive-pack will error out, instead of
+       accepting the pack file. If not set or set to 0, then the size
+       is unlimited.
+
 receive.denyDeletes::
        If set to true, git-receive-pack will deny a ref update that deletes
        the ref. Use this to prevent such a ref deletion via a push.
index 7a4e0555205be5d8bb26e5a183f1552cc057637a..1b4b65d6657b003079e0407d1da8d8c239933267 100644 (file)
@@ -87,6 +87,8 @@ OPTIONS
        Specifying 0 will cause Git to auto-detect the number of CPU's
        and use maximum 3 threads.
 
+--max-input-size=<size>::
+       Die, if the pack is larger than <size>.
 
 Note
 ----
index 000ee8dba2ab3069e0459defe3df9d7140541a59..0ccd5fbc781deb3adcca4d28ec8a4ed0d9db9977 100644 (file)
@@ -33,6 +33,9 @@ post-update hooks found in the Documentation/howto directory.
 option, which tells it if updates to a ref should be denied if they
 are not fast-forwards.
 
+A number of other receive.* config options are available to tweak
+its behavior, see linkgit:git-config[1].
+
 OPTIONS
 -------
 <directory>::
index 3e887d16109c48f4f906d8a7c5c0c860bbd6123d..b3de50d7106819f4dee92b3508c7713d170832ce 100644 (file)
@@ -44,6 +44,9 @@ OPTIONS
 --strict::
        Don't write objects with broken content or links.
 
+--max-input-size=<size>::
+       Die, if the pack is larger than <size>.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 1d2ea583a45507935ec2007d6c44f5e968a1aed5..4a8b4aebbac7d3992082b4440957f11aeffc39f5 100644 (file)
@@ -87,6 +87,7 @@ static struct progress *progress;
 static unsigned char input_buffer[4096];
 static unsigned int input_offset, input_len;
 static off_t consumed_bytes;
+static off_t max_input_size;
 static unsigned deepest_delta;
 static git_SHA_CTX input_ctx;
 static uint32_t input_crc32;
@@ -297,6 +298,8 @@ static void use(int bytes)
        if (signed_add_overflows(consumed_bytes, bytes))
                die(_("pack too large for current definition of off_t"));
        consumed_bytes += bytes;
+       if (max_input_size && consumed_bytes > max_input_size)
+               die(_("pack exceeds maximum allowed size"));
 }
 
 static const char *open_pack_file(const char *pack_name)
@@ -1714,6 +1717,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                        opts.off32_limit = strtoul(c+1, &c, 0);
                                if (*c || opts.off32_limit & 0x80000000)
                                        die(_("bad %s"), arg);
+                       } else if (skip_prefix(arg, "--max-input-size=", &arg)) {
+                               max_input_size = strtoumax(arg, NULL, 10);
                        } else
                                usage(index_pack_usage);
                        continue;
index 011db00d31709408a21b97abfb5742b97b54238d..f1ce05ce282361d8c49b1a447db90c13d5186674 100644 (file)
@@ -46,6 +46,7 @@ static int transfer_unpack_limit = -1;
 static int advertise_atomic_push = 1;
 static int advertise_push_options;
 static int unpack_limit = 100;
+static off_t max_input_size;
 static int report_status;
 static int use_sideband;
 static int use_atomic;
@@ -212,6 +213,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "receive.maxinputsize") == 0) {
+               max_input_size = git_config_int64(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -1648,6 +1654,9 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                if (fsck_objects)
                        argv_array_pushf(&child.args, "--strict%s",
                                fsck_msg_types.buf);
+               if (max_input_size)
+                       argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
+                               (uintmax_t)max_input_size);
                child.no_stdout = 1;
                child.err = err_fd;
                child.git_cmd = 1;
@@ -1676,6 +1685,9 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                                fsck_msg_types.buf);
                if (!reject_thin)
                        argv_array_push(&child.args, "--fix-thin");
+               if (max_input_size)
+                       argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
+                               (uintmax_t)max_input_size);
                child.out = -1;
                child.err = err_fd;
                child.git_cmd = 1;
index 172470bf241e4da731c5741567555d7e01691c74..4532aa083154ae7e9be07014d3761821fa6c4ddc 100644 (file)
@@ -19,6 +19,7 @@ static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]
 static unsigned char buffer[4096];
 static unsigned int offset, len;
 static off_t consumed_bytes;
+static off_t max_input_size;
 static git_SHA_CTX ctx;
 static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
 
@@ -87,6 +88,8 @@ static void use(int bytes)
        if (signed_add_overflows(consumed_bytes, bytes))
                die("pack too large for current definition of off_t");
        consumed_bytes += bytes;
+       if (max_input_size && consumed_bytes > max_input_size)
+               die(_("pack exceeds maximum allowed size"));
 }
 
 static void *get_data(unsigned long size)
@@ -550,6 +553,10 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
                                len = sizeof(*hdr);
                                continue;
                        }
+                       if (skip_prefix(arg, "--max-input-size=", &arg)) {
+                               max_input_size = strtoumax(arg, NULL, 10);
+                               continue;
+                       }
                        usage(unpack_usage);
                }
 
diff --git a/t/t5546-receive-limits.sh b/t/t5546-receive-limits.sh
new file mode 100755 (executable)
index 0000000..10cb0be
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='check receive input limits'
+. ./test-lib.sh
+
+# Let's run tests with different unpack limits: 1 and 10000
+# When the limit is 1, `git receive-pack` will call `git index-pack`.
+# When the limit is 10000, `git receive-pack` will call `git unpack-objects`.
+
+test_pack_input_limit () {
+       case "$1" in
+       index) unpack_limit=1 ;;
+       unpack) unpack_limit=10000 ;;
+       esac
+
+       test_expect_success 'prepare destination repository' '
+               rm -fr dest &&
+               git --bare init dest
+       '
+
+       test_expect_success "set unpacklimit to $unpack_limit" '
+               git --git-dir=dest config receive.unpacklimit "$unpack_limit"
+       '
+
+       test_expect_success 'setting receive.maxInputSize to 512 rejects push' '
+               git --git-dir=dest config receive.maxInputSize 512 &&
+               test_must_fail git push dest HEAD
+       '
+
+       test_expect_success 'bumping limit to 4k allows push' '
+               git --git-dir=dest config receive.maxInputSize 4k &&
+               git push dest HEAD
+       '
+
+       test_expect_success 'prepare destination repository (again)' '
+               rm -fr dest &&
+               git --bare init dest
+       '
+
+       test_expect_success 'lifting the limit allows push' '
+               git --git-dir=dest config receive.maxInputSize 0 &&
+               git push dest HEAD
+       '
+}
+
+test_expect_success "create known-size (1024 bytes) commit" '
+       test-genrandom foo 1024 >one-k &&
+       git add one-k &&
+       test_commit one-k
+'
+
+test_pack_input_limit index
+test_pack_input_limit unpack
+
+test_done