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