Git fork

Merge branch 'ps/upload-pack-oom-protection' into maint-2.51

A broken or malicious "git fetch" can say that it has the same
object for many many times, and the upload-pack serving it can
exhaust memory storing them redundantly, which has been corrected.

* ps/upload-pack-oom-protection:
upload-pack: don't ACK non-commits repeatedly in protocol v2
t5530: modernize tests

+51 -36
+42 -26
t/t5530-upload-pack-error.sh
··· 4 4 5 5 . ./test-lib.sh 6 6 7 - D=$(pwd) 8 - 9 7 corrupt_repo () { 10 8 object_sha1=$(git rev-parse "$1") && 11 9 ob=$(expr "$object_sha1" : "\(..\)") && ··· 21 19 test_tick && 22 20 echo changed >file && 23 21 git commit -a -m changed && 24 - corrupt_repo HEAD:file 25 - 26 - ' 27 - 28 - test_expect_success 'fsck fails' ' 22 + corrupt_repo HEAD:file && 29 23 test_must_fail git fsck 30 24 ' 31 25 ··· 40 34 ' 41 35 42 36 test_expect_success 'corrupt repo differently' ' 43 - 44 37 git hash-object -w file && 45 - corrupt_repo HEAD^^{tree} 46 - 47 - ' 48 - 49 - test_expect_success 'fsck fails' ' 38 + corrupt_repo HEAD^^{tree} && 50 39 test_must_fail git fsck 51 40 ' 52 - test_expect_success 'upload-pack fails due to error in rev-list' ' 53 41 42 + test_expect_success 'upload-pack fails due to error in rev-list' ' 54 43 printf "%04xwant %s\n%04xshallow %s00000009done\n0000" \ 55 44 $(($hexsz + 10)) $(git rev-parse HEAD) \ 56 45 $(($hexsz + 12)) $(git rev-parse HEAD^) >input && ··· 59 48 ' 60 49 61 50 test_expect_success 'upload-pack fails due to bad want (no object)' ' 62 - 63 51 printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \ 64 52 $(($hexsz + 29)) $(test_oid deadbeef) >input && 65 53 test_must_fail git upload-pack . <input >output 2>output.err && ··· 69 57 ' 70 58 71 59 test_expect_success 'upload-pack fails due to bad want (not tip)' ' 72 - 73 60 oid=$(echo an object we have | git hash-object -w --stdin) && 74 61 printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \ 75 62 $(($hexsz + 29)) "$oid" >input && ··· 80 67 ' 81 68 82 69 test_expect_success 'upload-pack fails due to error in pack-objects enumeration' ' 83 - 84 70 printf "%04xwant %s\n00000009done\n0000" \ 85 71 $((hexsz + 10)) $(git rev-parse HEAD) >input && 86 72 test_must_fail git upload-pack . <input >/dev/null 2>output.err && ··· 105 91 test_cmp expect actual 106 92 ' 107 93 108 - test_expect_success 'create empty repository' ' 109 - 110 - mkdir foo && 111 - cd foo && 112 - git init 113 - 94 + test_expect_success 'fetch fails' ' 95 + git init foo && 96 + test_must_fail git -C foo fetch .. main 114 97 ' 115 98 116 - test_expect_success 'fetch fails' ' 117 - 118 - test_must_fail git fetch .. main 99 + test_expect_success 'upload-pack ACKs repeated non-commit objects repeatedly (protocol v0)' ' 100 + commit_id=$(git rev-parse HEAD) && 101 + tree_id=$(git rev-parse HEAD^{tree}) && 102 + test-tool pkt-line pack >request <<-EOF && 103 + want $commit_id 104 + 0000 105 + have $tree_id 106 + have $tree_id 107 + 0000 108 + EOF 109 + git upload-pack --stateless-rpc . <request >actual && 110 + depacketize <actual >actual.raw && 111 + grep ^ACK actual.raw >actual.acks && 112 + cat >expect <<-EOF && 113 + ACK $tree_id 114 + ACK $tree_id 115 + EOF 116 + test_cmp expect actual.acks 117 + ' 119 118 119 + test_expect_success 'upload-pack ACKs repeated non-commit objects once only (protocol v2)' ' 120 + commit_id=$(git rev-parse HEAD) && 121 + tree_id=$(git rev-parse HEAD^{tree}) && 122 + test-tool pkt-line pack >request <<-EOF && 123 + command=fetch 124 + object-format=$(test_oid algo) 125 + 0001 126 + want $commit_id 127 + have $tree_id 128 + have $tree_id 129 + 0000 130 + EOF 131 + GIT_PROTOCOL=version=2 git upload-pack . <request >actual && 132 + depacketize <actual >actual.raw && 133 + grep ^ACK actual.raw >actual.acks && 134 + echo "ACK $tree_id" >expect && 135 + test_cmp expect actual.acks 120 136 ' 121 137 122 138 test_done
+9 -10
upload-pack.c
··· 476 476 477 477 static int do_got_oid(struct upload_pack_data *data, const struct object_id *oid) 478 478 { 479 - int we_knew_they_have = 0; 480 479 struct object *o = parse_object_with_flags(the_repository, oid, 481 480 PARSE_OBJECT_SKIP_HASH_CHECK | 482 481 PARSE_OBJECT_DISCARD_TREE); 483 482 484 483 if (!o) 485 484 die("oops (%s)", oid_to_hex(oid)); 485 + 486 486 if (o->type == OBJ_COMMIT) { 487 487 struct commit_list *parents; 488 488 struct commit *commit = (struct commit *)o; 489 - if (o->flags & THEY_HAVE) 490 - we_knew_they_have = 1; 491 - else 492 - o->flags |= THEY_HAVE; 489 + 493 490 if (!data->oldest_have || (commit->date < data->oldest_have)) 494 491 data->oldest_have = commit->date; 495 492 for (parents = commit->parents; ··· 497 494 parents = parents->next) 498 495 parents->item->object.flags |= THEY_HAVE; 499 496 } 500 - if (!we_knew_they_have) { 501 - add_object_array(o, NULL, &data->have_obj); 502 - return 1; 503 - } 504 - return 0; 497 + 498 + if (o->flags & THEY_HAVE) 499 + return 0; 500 + o->flags |= THEY_HAVE; 501 + 502 + add_object_array(o, NULL, &data->have_obj); 503 + return 1; 505 504 } 506 505 507 506 static int got_oid(struct upload_pack_data *data,