Commit 349874474896d69979338961ee5813c146f561fb

Patrick Steinhardt 2020-06-30T10:13:26

refdb: avoid unlimited spinning in case of symref cycles To determine whether another reflog entry needs to be written for HEAD on a reference update, we need to see whether HEAD directly or indirectly points to the reference we're updating. The resolve logic is currently completely unbounded except an error occurs, which effectively means that we'd be spinning forever in case we have a symref loop in the repository refdb. Let's fix the issue by using `git_refdb_resolve` instead, which is always bounded.

diff --git a/src/refdb.c b/src/refdb.c
index 6879b6a..fb86d5c 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -326,7 +326,7 @@ int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *
 
 int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref)
 {
-	git_reference *head = NULL, *peeled = NULL;
+	git_reference *head = NULL, *resolved = NULL;
 	const char *name;
 	int error;
 
@@ -344,22 +344,15 @@ int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_refere
 		goto out;
 
 	/* Go down the symref chain until we find the branch */
-	while (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC) {
-		if ((error = git_refdb_lookup(&peeled, db, git_reference_symbolic_target(head))) < 0)
-			break;
-
-		git_reference_free(head);
-		head = peeled;
-		peeled = NULL;
-	}
-
-	if (error < 0) {
+	if ((error = git_refdb_resolve(&resolved, db, git_reference_symbolic_target(head), -1)) < 0) {
 		if (error != GIT_ENOTFOUND)
 			goto out;
 		error = 0;
 		name = git_reference_symbolic_target(head);
+	} else if (git_reference_type(resolved) == GIT_REFERENCE_SYMBOLIC) {
+		name = git_reference_symbolic_target(resolved);
 	} else {
-		name = git_reference_name(head);
+		name = git_reference_name(resolved);
 	}
 
 	if (strcmp(name, ref->name))
@@ -368,7 +361,7 @@ int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_refere
 	*out = 1;
 
 out:
-	git_reference_free(peeled);
+	git_reference_free(resolved);
 	git_reference_free(head);
 	return error;
 }