remote: make match_refs() copy src ref before assigning to peer_ref

In some instances, match_refs() sets the peer_ref field of refs in the
dst list such that it points to a ref in the src list. This prevents
callers from freeing both the src and dst lists, as doing so would cause
a double-free since free_refs() frees the peer_ref.

As well, the following configuration causes two refs in the dst list to
have the same peer_ref, which can also lead to a double-free:

  push = refs/heads/master:refs/heads/backup
  push = refs/heads/master:refs/heads/master

Existing callers of match_heads() call it only once and then terminate,
w/o ever bothering to free the src or dst lists, so this is not
currently a problem.

This patch modifies match_refs() to first copy any refs it plucks from
the src list before assigning them as a peer_ref. This allows
builtin-remote, a future caller, to free the src and dst lists.

Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jay Soffian 2009-02-25 03:32:16 -05:00 committed by Junio C Hamano
parent 4229f1fa32
commit cdf690e53b

View file

@ -936,6 +936,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
struct refspec *rs)
{
struct ref *matched_src, *matched_dst;
int copy_src;
const char *dst_value = rs->dst;
char *dst_guess;
@ -946,6 +947,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
matched_src = matched_dst = NULL;
switch (count_refspec_match(rs->src, src, &matched_src)) {
case 1:
copy_src = 1;
break;
case 0:
/* The source could be in the get_sha1() format
@ -955,6 +957,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
matched_src = try_explicit_object_name(rs->src);
if (!matched_src)
return error("src refspec %s does not match any.", rs->src);
copy_src = 0;
break;
default:
return error("src refspec %s matches more than one.", rs->src);
@ -1000,7 +1003,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
return error("dst ref %s receives from more than one src.",
matched_dst->name);
else {
matched_dst->peer_ref = matched_src;
matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
matched_dst->force = rs->force;
}
return 0;
@ -1108,7 +1111,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
dst_peer = make_linked_ref(dst_name, dst_tail);
hashcpy(dst_peer->new_sha1, src->new_sha1);
}
dst_peer->peer_ref = src;
dst_peer->peer_ref = copy_ref(src);
dst_peer->force = pat->force;
free_name:
free(dst_name);