Skip to content

Commit 7e42fac

Browse files
pks-tgitster
authored andcommitted
refs: don't store peeled object IDs for invalid tags
Both the "files" and "reftable" backend store peeled object IDs for references that point to tags: - The "files" backend stores the value when packing refs, where each peeled object ID is prefixed with "^". - The "reftable" backend stores the value whenever writing a new reference that points to a tag via a special ref record type. Both of these backends use `peel_object()` to find the peeled object ID. But as explained in the preceding commit, that function does not detect the case where the tag's tagged object and its claimed type mismatch. The consequence of storing these bogus peeled object IDs is that we're less likely to detect such corruption in other parts of Git. git-for-each-ref(1) for example does not notice anymore that the tag is broken when using "--format=%(*objectname)" to dereference tags. One could claim that this is good, because it still allows us to mostly use the tag as intended. But the biggest problem here is that we now have different behaviour for such a broken tag depending on whether or not we have its peeled value in the refdb. Fix the issue by verifying the object type when peeling the object. If that verification fails we simply skip storing the peeled value in either of the reference formats. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 23d56d5 commit 7e42fac

File tree

4 files changed

+63
-2
lines changed

4 files changed

+63
-2
lines changed

refs/packed-backend.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1528,7 +1528,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
15281528
} else {
15291529
struct object_id peeled;
15301530
int peel_error = peel_object(refs->base.repo, &update->new_oid,
1531-
&peeled, 0);
1531+
&peeled, PEEL_OBJECT_VERIFY_OBJECT_TYPE);
15321532

15331533
if (write_packed_entry(out, update->refname,
15341534
&update->new_oid,

refs/reftable-backend.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1631,7 +1631,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
16311631
ref.refname = (char *)u->refname;
16321632
ref.update_index = ts;
16331633

1634-
peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
1634+
peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
1635+
PEEL_OBJECT_VERIFY_OBJECT_TYPE);
16351636
if (!peel_error) {
16361637
ref.value_type = REFTABLE_REF_VAL2;
16371638
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);

t/pack-refs-tests.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,4 +428,36 @@ do
428428
'
429429
done
430430

431+
test_expect_success 'pack-refs does not store invalid peeled tag value' '
432+
test_when_finished rm -rf repo &&
433+
git init repo &&
434+
(
435+
cd repo &&
436+
git commit --allow-empty --message initial &&
437+
438+
echo garbage >blob-content &&
439+
blob_id=$(git hash-object -w -t blob blob-content) &&
440+
441+
# Write an invalid tag into the object database. The tag itself
442+
# is well-formed, but the tagged object is a blob while we
443+
# claim that it is a commit.
444+
cat >tag-content <<-EOF &&
445+
object $blob_id
446+
type commit
447+
tag bad-tag
448+
tagger C O Mitter <[email protected]> 1112354055 +0200
449+
450+
annotated
451+
EOF
452+
tag_id=$(git hash-object -w -t tag tag-content) &&
453+
git update-ref refs/tags/bad-tag "$tag_id" &&
454+
455+
# The packed-refs file should not contain the peeled object ID.
456+
# If it did this would cause commands that use the peeled value
457+
# to not notice this corrupted tag.
458+
git pack-refs --all &&
459+
test_grep ! "^\^" .git/packed-refs
460+
)
461+
'
462+
431463
test_done

t/t0610-reftable-basics.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,4 +1135,32 @@ test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
11351135
test_cmp expect actual
11361136
'
11371137

1138+
test_expect_success 'writes do not persist peeled value for invalid tags' '
1139+
test_when_finished rm -rf repo &&
1140+
git init repo &&
1141+
(
1142+
cd repo &&
1143+
git commit --allow-empty --message initial &&
1144+
1145+
# We cannot easily verify that the peeled value is not stored
1146+
# in the tables. Instead, we test this indirectly: we create
1147+
# two tags that both point to the same object, but they claim
1148+
# different object types. If we parse both tags we notice that
1149+
# the parsed tagged object has a mismatch between the two tags
1150+
# and bail out.
1151+
#
1152+
# If we instead use the persisted peeled value we would not
1153+
# even parse the tags. As such, we would not notice the
1154+
# discrepancy either and thus listing these tags would succeed.
1155+
git tag tag-1 -m "tag 1" &&
1156+
git cat-file tag tag-1 >raw-tag &&
1157+
sed "s/^type commit$/type blob/" <raw-tag >broken-tag &&
1158+
broken_tag_id=$(git hash-object -w -t tag broken-tag) &&
1159+
git update-ref refs/tags/tag-2 $broken_tag_id &&
1160+
1161+
test_must_fail git for-each-ref --format="%(*objectname)" refs/tags/ 2>err &&
1162+
test_grep "bad tag pointer" err
1163+
)
1164+
'
1165+
11381166
test_done

0 commit comments

Comments
 (0)