From: Christian Couder <hidden> Date: 2016-08-15 19:58:36
In https://public-inbox.org/git/20150612182045.GA23698%40peff.net/,
Peff sent a patch that is used by GitHub to abort `git receive-pack`
when the size of the pack we receive is bigger than a configured
limit.
GitLab is interested in using the same approach and in standardizing
the error messages the user could get back.
So I rebased Peff's patch to the current master, refreshed it a bit,
split it, and added the missing --max-input-size=<size> option to
`git unpack-objects` - to make it work for all `transfer.unpacklimit`
values - in a new patch.
There is no documentation yet for the `--max-input-size=<size>`
options added to `git index-pack` and `git unpack-objects`, nor for
the new `receive.maxsize` config option.
I kept Peff as the author of the patches that are made mostly from his
patch, but I added my Signed-off-by to them.
Christian Couder (1):
unpack-objects: add --max-input-size=<size> option
Jeff King (2):
index-pack: add --max-input-size=<size> option
receive-pack: allow a maximum input size to be specified
builtin/index-pack.c | 5 +++++
builtin/receive-pack.c | 12 ++++++++++++
builtin/unpack-objects.c | 7 +++++++
t/t5546-push-limits.sh | 47 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 71 insertions(+)
create mode 100755 t/t5546-push-limits.sh
--
2.10.0.rc0.4.g229e32c.dirty
From: Christian Couder <hidden> Date: 2016-08-15 19:59:05
From: Jeff King <redacted>
When receiving a pack-file, it can be useful to abort the
`git index-pack`, if the pack-file is too big.
Signed-off-by: Jeff King <redacted>
Signed-off-by: Christian Couder <redacted>
---
builtin/index-pack.c | 5 +++++
1 file changed, 5 insertions(+)
@@ -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"));}staticconstchar*open_pack_file(constchar*pack_name)
From: Christian Couder <hidden> Date: 2016-08-15 19:59:08
When receiving a pack-file, it can be useful to abort the
`git unpack-objects`, if the pack-file is too big.
Signed-off-by: Christian Couder <redacted>
---
builtin/unpack-objects.c | 7 +++++++
1 file changed, 7 insertions(+)
@@ -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"));}staticvoid*get_data(unsignedlongsize)
From: Christian Couder <hidden> Date: 2016-08-15 19:59:19
From: Jeff King <redacted>
Receive-pack feeds its input to either index-pack or
unpack-objects, which will happily accept as many bytes as
a sender is willing to provide. Let's allow an arbitrary
cutoff point where we will stop writing bytes to disk.
What has already been written to disk can be cleaned
outside of receive-pack.
Signed-off-by: Jeff King <redacted>
Signed-off-by: Christian Couder <redacted>
---
builtin/receive-pack.c | 12 ++++++++++++
t/t5546-push-limits.sh | 47 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+)
create mode 100755 t/t5546-push-limits.sh
From: Jeff King <hidden> Date: 2016-08-15 20:11:10
On Mon, Aug 15, 2016 at 09:57:27PM +0200, Christian Couder wrote:
From: Jeff King <redacted>
When receiving a pack-file, it can be useful to abort the
`git index-pack`, if the pack-file is too big.
Not much rationale here. I guess because it is all in the 3rd patch,
which ties it into receive-pack. I'm not sure it's worth repeating. I
guess it could all be squished back into one patch. I'm OK either way.
@@ -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"));
Looks good. I see you marked it for translation, which makes sense.
On the original, I waffled on whether to share the size with the user in
the message. I didn't want to encourage people with "oh, if it's under
2G it must be OK, then!". Because really 2G was meant to be a "you
really shouldn't get this high, and we will unceremoniously dump your
push if you do".
max_input_size is an off_t, but your parse only up to ULONG_MAX here.
For my purposes in the original patch, this was OK, as we set it at 2GB,
which works everywhere (and also, GitHub systems all have 64-bit "long"
these days). But somebody on a 32-bit system could not set this to 4GB,
even though I think index-pack could otherwise handle it. We seem to use
strtoumax() elsewhere, so that's probably a good match (technically it
can overflow an off_t, but in practice this value comes from the admin
and they will set something sane).
-Peff
From: Jeff King <hidden> Date: 2016-08-15 20:11:39
On Mon, Aug 15, 2016 at 09:57:28PM +0200, Christian Couder wrote:
When receiving a pack-file, it can be useful to abort the
`git unpack-objects`, if the pack-file is too big.
Signed-off-by: Christian Couder <redacted>
Same remarks here as on the last patch, including strtoumax. :)
-Peff
From: Jeff King <hidden> Date: 2016-08-15 20:40:43
On Mon, Aug 15, 2016 at 09:57:29PM +0200, Christian Couder wrote:
From: Jeff King <redacted>
Receive-pack feeds its input to either index-pack or
unpack-objects, which will happily accept as many bytes as
a sender is willing to provide. Let's allow an arbitrary
cutoff point where we will stop writing bytes to disk.
What has already been written to disk can be cleaned
outside of receive-pack.
This second paragraph hints at a related problem.
Git is generally happy to leave tmp_pack_* around to be cleaned up later
next time git-gc runs. Including its default 2-week grace time.
So imagine that tries to "git push" in a loop. And each time they push,
you say "nope, that's too big". And each time you acquire a new 2GB
tmp_pack file. If your goal was to prevent somebody from streaming
straight to your filesystem and filling up your disk, then it wasn't
very successful. :)
The simple fix is to call register_tempfile() in open_pack_file(), and
just have index-pack clean up the file on its way out.
But there are harder cases. For instance, imagine somebody pushes a
500MB file, and you have a pre-receive hook that says "too big; I won't
accept this". And then they push in a loop, as before. You've accepted
the incoming pack into the repository by the time the pre-receive runs.
You can't just delete it, because you don't know if other simultaneous
processes have started to depend on the objects.
To solve that, I have patches that put incoming packfiles into a
"quarantine" area, then run the connectivity check and pre-receive hooks
with the quarantine accessible via GIT_ALTERNATE_OBJECT_DIRECTORIES. And
then we either move the quarantine packs into the real repo, or blow
away the tmpdir, depending on whether the hooks said the objects were
OK.
Those are patches I plan to share upstream but just haven't gotten
around to yet.
And here, PRIuMAX and uintmax_t. Or perhaps simpler, just store the
value as a string here and pass it on to index-pack (which would then
need to learn to handle suffixes like "2g"). We do a similar trick in
repack; see b861e23 (repack: propagate pack-objects options as strings,
2014-01-22).
We're going to do basically the same battery of tests against an
unpacklimit of "1" (to catch index-pack) and of "10" (to catch
unpack-objects). It might be clearer to just have a for-loop like:
for unpacklimit in 1 100
do
test_expect_success 'create remote repository' '
rm -rf dest &&
git init --bare dest &&
git -C dest config receive.unpacklimit $unpacklimit
'
test_expect_success 'receive.maxsize rejects push' '
git -C dest config receive.maxsize 512 &&
test_must_fail git push dest HEAD &&
'
test_expect_success 'bumping limit allows push' '
git -C dest config receive.maxsize 4k &&
git push dest HEAD
'
done
and it's probably worth a comment at the top of the loop explaining what
the heck those numbers mean. :)
-Peff