tag: avoid a double-free when parsing tags without a tagger field The v0.99 tag in the Git repo triggers this behavior: http://git.kernel.org/?p=git/git.git;a=tag;h=d6602ec5194c87b0fc87103ca4d67251c76f233a Ideally, we'd allow the tag to be instantiated even though the tagger field is missing, but this at the very least prevents libgit2 from crashing. To test this bug, a new repository has been added based on the test branch in testrepo.git. It contains a "e90810b" tag that looks like this: object e90810b8df3e80c413d903f631643c716887138d type commit tag e90810b This is a very simple tag.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
diff --git a/src/tag.c b/src/tag.c
index fc9f8b5..08ebb71 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -126,11 +126,8 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
if (tag->tagger == NULL)
return GIT_ENOMEM;
- if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n')) != 0) {
- free(tag->tag_name);
- git_signature_free(tag->tagger);
+ if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n')) != 0)
return git__rethrow(error, "Failed to parse tag");
- }
if( *buffer != '\n' )
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. No new line before message");
diff --git a/tests/resources/bad_tag.git/HEAD b/tests/resources/bad_tag.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/bad_tag.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/bad_tag.git/config b/tests/resources/bad_tag.git/config
new file mode 100644
index 0000000..2f89580
--- /dev/null
+++ b/tests/resources/bad_tag.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
new file mode 100644
index 0000000..c404aa1
Binary files /dev/null and b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx differ
diff --git a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
new file mode 100644
index 0000000..90eac50
Binary files /dev/null and b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack differ
diff --git a/tests/resources/bad_tag.git/packed-refs b/tests/resources/bad_tag.git/packed-refs
new file mode 100644
index 0000000..f9fd2fd
--- /dev/null
+++ b/tests/resources/bad_tag.git/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled
+eda9f45a2a98d4c17a09d681d88569fa4ea91755 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests/t08-tag.c b/tests/t08-tag.c
index b0d4af8..079a9e0 100644
--- a/tests/t08-tag.c
+++ b/tests/t08-tag.c
@@ -30,6 +30,7 @@
static const char *tag1_id = "b25fa35b38051e4ae45d4222e795f9df2e43f1d1";
static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+static const char *bad_tag_id = "eda9f45a2a98d4c17a09d681d88569fa4ea91755";
BEGIN_TEST(read0, "read and parse a tag from the repository")
git_repository *repo;
@@ -106,6 +107,22 @@ BEGIN_TEST(read2, "list all tag names from the repository matching a specified p
git_repository_free(repo);
END_TEST
+#define BAD_TAG_REPOSITORY_FOLDER TEST_RESOURCES "/bad_tag.git/"
+
+BEGIN_TEST(read3, "read and parse a tag without a tagger field")
+ git_repository *repo;
+ git_tag *bad_tag;
+ git_oid id;
+
+ must_pass(git_repository_open(&repo, BAD_TAG_REPOSITORY_FOLDER));
+
+ git_oid_fromstr(&id, bad_tag_id);
+
+ must_fail(git_tag_lookup(&bad_tag, repo, &id));
+
+ git_repository_free(repo);
+END_TEST
+
#define TAGGER_NAME "Vicent Marti"
#define TAGGER_EMAIL "vicent@github.com"
@@ -304,6 +321,7 @@ BEGIN_SUITE(tag)
ADD_TEST(read0);
ADD_TEST(read1);
ADD_TEST(read2);
+ ADD_TEST(read3);
ADD_TEST(write0);
ADD_TEST(write2);