diff --git a/apply.c b/apply.c index 2b7cd930ef..850604c191 100644 --- a/apply.c +++ b/apply.c @@ -386,9 +386,19 @@ static void say_patch_name(FILE *output, const char *fmt, struct patch *patch) #define SLOP (16) +/* + * apply.c isn't equipped to handle arbitrarily large patches, because + * it intermingles `unsigned long` with `int` for the type used to store + * buffer lengths. + * + * Only process patches that are just shy of 1 GiB large in order to + * avoid any truncation or overflow issues. + */ +#define MAX_APPLY_SIZE (1024UL * 1024 * 1023) + static int read_patch_file(struct strbuf *sb, int fd) { - if (strbuf_read(sb, fd, 0) < 0) + if (strbuf_read(sb, fd, 0) < 0 || sb->len >= MAX_APPLY_SIZE) return error_errno("git apply: failed to read"); /* diff --git a/t/t4141-apply-too-large.sh b/t/t4141-apply-too-large.sh new file mode 100755 index 0000000000..58742d4fc5 --- /dev/null +++ b/t/t4141-apply-too-large.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +test_description='git apply with too-large patch' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_expect_success EXPENSIVE 'git apply rejects patches that are too large' ' + sz=$((1024 * 1024 * 1023)) && + { + cat <<-\EOF && + diff --git a/file b/file + new file mode 100644 + --- /dev/null + +++ b/file + @@ -0,0 +1 @@ + EOF + test-tool genzeros + } | test_copy_bytes $sz | test_must_fail git apply 2>err && + grep "git apply: failed to read" err +' + +test_done