Skip to content

Commit 23d56d5

Browse files
pks-tgitster
authored andcommitted
object: add flag to peel_object() to verify object type
When peeling a tag to a non-tag object we repeatedly call `parse_object()` on the tagged object until we find the first object that isn't a tag. While this feels sensible at first, there is a big catch here: `parse_object()` doesn't actually verify the type of the tagged object. The relevant code path here eventually ends up in `parse_tag_buffer()`. Here, we parse the various fields of the tag, including the "type". Once we've figured out the type and the tagged object ID, we call one of the `lookup_${type}()` functions for whatever type we have found. There is two possible outcomes in the successful case: 1. The object is already part of our cached objects. In that case we double-check whether the type we're trying to look up matches the type that was cached. 2. The object is _not_ part of our cached objects. In that case, we simply create a new object with the expected type, but we don't parse that object. In the first case we might notice type mismatches, but only in the case where our cache has the object with the correct type. In the second case, we'll blindly assume that the type is correct and then go with it. We'll only notice that the type might be wrong when we try to parse the object at a later point. Now arguably, we could change `parse_tag_buffer()` to verify the tagged object's type for us. But that would have the effect that such a tag cannot be parsed at all anymore, and we have a small bunch of tests for exactly this case that assert we still can open such tags. So this change does not feel like something we can retroactively tighten, even though one shouldn't ever hit such corrupted tags. Instead, add a new `flags` field to `peel_object()` that allows the caller to opt in to strict object verification. This will be wired up at a subset of callsites over the next few commits. Note that this change also inlines `deref_tag_noverify()`. There's only been two callsites of that function, the one we're changing and one in our test helpers. The latter callsite can trivially use `deref_tag()` instead, so by inlining the function we avoid having to pass down the flag. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2fba9f2 commit 23d56d5

File tree

9 files changed

+38
-25
lines changed

9 files changed

+38
-25
lines changed

object.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,12 @@ struct object *lookup_object_by_type(struct repository *r,
209209

210210
enum peel_status peel_object(struct repository *r,
211211
const struct object_id *name,
212-
struct object_id *oid)
212+
struct object_id *oid,
213+
unsigned flags)
213214
{
214215
struct object *o = lookup_unknown_object(r, name);
215216

216-
if (o->type == OBJ_NONE) {
217+
if (o->type == OBJ_NONE || flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
217218
int type = odb_read_object_info(r->objects, name, NULL);
218219
if (type < 0 || !object_as_type(o, type, 0))
219220
return PEEL_INVALID;
@@ -222,7 +223,20 @@ enum peel_status peel_object(struct repository *r,
222223
if (o->type != OBJ_TAG)
223224
return PEEL_NON_TAG;
224225

225-
o = deref_tag_noverify(r, o);
226+
while (o && o->type == OBJ_TAG) {
227+
o = parse_object(r, &o->oid);
228+
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged) {
229+
o = ((struct tag *)o)->tagged;
230+
231+
if (flags & PEEL_OBJECT_VERIFY_OBJECT_TYPE) {
232+
int type = odb_read_object_info(r->objects, &o->oid, NULL);
233+
if (type < 0 || !object_as_type(o, type, 0))
234+
return PEEL_INVALID;
235+
}
236+
} else {
237+
o = NULL;
238+
}
239+
}
226240
if (!o)
227241
return PEEL_INVALID;
228242

object.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,17 @@ enum peel_status {
287287
PEEL_BROKEN = -4
288288
};
289289

290+
enum peel_object_flags {
291+
/*
292+
* Always verify the object type, even in the case where the looked-up
293+
* object already has an object type. This can be useful when the
294+
* stored object type may be invalid. One such case is when looking up
295+
* objects via tags, where we blindly trust the object type declared by
296+
* the tag.
297+
*/
298+
PEEL_OBJECT_VERIFY_OBJECT_TYPE = (1 << 0),
299+
};
300+
290301
/*
291302
* Peel the named object; i.e., if the object is a tag, resolve the
292303
* tag recursively until a non-tag is found. If successful, store the
@@ -295,7 +306,9 @@ enum peel_status {
295306
* and leave oid unchanged.
296307
*/
297308
enum peel_status peel_object(struct repository *r,
298-
const struct object_id *name, struct object_id *oid);
309+
const struct object_id *name,
310+
struct object_id *oid,
311+
unsigned flags);
299312

300313
struct object_list *object_list_insert(struct object *item,
301314
struct object_list **list_p);

ref-filter.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2581,7 +2581,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
25812581
if (need_tagged) {
25822582
if (!is_null_oid(&ref->peeled_oid)) {
25832583
oidcpy(&oi_deref.oid, &ref->peeled_oid);
2584-
} else if (!peel_object(the_repository, &obj->oid, &oi_deref.oid)) {
2584+
} else if (!peel_object(the_repository, &oi.oid, &oi_deref.oid, 0)) {
25852585
/* We managed to peel the object ourselves. */
25862586
} else {
25872587
die("bad tag");

refs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2332,7 +2332,7 @@ int reference_get_peeled_oid(struct repository *repo,
23322332
return 0;
23332333
}
23342334

2335-
return peel_object(repo, ref->oid, peeled_oid) ? -1 : 0;
2335+
return peel_object(repo, ref->oid, peeled_oid, 0) ? -1 : 0;
23362336
}
23372337

23382338
int refs_update_symref(struct ref_store *refs, const char *ref,

refs/packed-backend.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,9 +1527,8 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
15271527
i++;
15281528
} else {
15291529
struct object_id peeled;
1530-
int peel_error = peel_object(refs->base.repo,
1531-
&update->new_oid,
1532-
&peeled);
1530+
int peel_error = peel_object(refs->base.repo, &update->new_oid,
1531+
&peeled, 0);
15331532

15341533
if (write_packed_entry(out, update->refname,
15351534
&update->new_oid,

refs/reftable-backend.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,7 +1631,7 @@ 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);
1634+
peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled, 0);
16351635
if (!peel_error) {
16361636
ref.value_type = REFTABLE_REF_VAL2;
16371637
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
@@ -2496,7 +2496,7 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
24962496
ref.refname = (char *)arg->refname;
24972497
ref.update_index = ts;
24982498

2499-
if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled)) {
2499+
if (!peel_object(arg->refs->base.repo, &arg->update_oid, &peeled, 0)) {
25002500
ref.value_type = REFTABLE_REF_VAL2;
25012501
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
25022502
memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);

t/helper/test-reach.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ int cmd__reach(int ac, const char **av)
6363
die("failed to resolve %s", buf.buf + 2);
6464

6565
orig = parse_object(r, &oid);
66-
peeled = deref_tag_noverify(the_repository, orig);
66+
peeled = deref_tag(the_repository, orig, NULL, 0);
6767

6868
if (!peeled)
6969
die("failed to load commit for input %s resulting in oid %s",

tag.c

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,6 @@ struct object *deref_tag(struct repository *r, struct object *o, const char *war
9494
return o;
9595
}
9696

97-
struct object *deref_tag_noverify(struct repository *r, struct object *o)
98-
{
99-
while (o && o->type == OBJ_TAG) {
100-
o = parse_object(r, &o->oid);
101-
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
102-
o = ((struct tag *)o)->tagged;
103-
else
104-
o = NULL;
105-
}
106-
return o;
107-
}
108-
10997
struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
11098
{
11199
struct object *obj = lookup_object(r, oid);

tag.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
1616
int parse_tag(struct tag *item);
1717
void release_tag_memory(struct tag *t);
1818
struct object *deref_tag(struct repository *r, struct object *, const char *, int);
19-
struct object *deref_tag_noverify(struct repository *r, struct object *);
2019
int gpg_verify_tag(const struct object_id *oid,
2120
const char *name_to_report, unsigned flags);
2221
struct object_id *get_tagged_oid(struct tag *tag);

0 commit comments

Comments
 (0)