reflog-walk: don't segfault on non-commit sha1's in the reflog

git reflog (ab)uses the log machinery to display its list of log entries. To do so it must fake commit parent information for the log walker. For refs in refs/heads this is no problem, as they should only ever point to commits. Tags and other refs however can point to anything, thus their reflog may contain non-commit objects. To avoid segfaulting, we check whether reflog entries are commits before feeding them to the log walker and skip any non-commits. This means that git reflog output will be incomplete for such refs, but that's one step up from segfaulting. A more complete solution would be to decouple git reflog from the log walker machinery. Signed-off-by: Dennis Kaarsemaker <dennis@kaarsemaker.net> Helped-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Dennis Kaarsemaker committed Jan 5, 2016 at 22:12 UTC aecad374ae7492cc7b2add5fa416d43e1f68c18e
2 files changed +24 -5
reflog-walk.c
+11 -5
@@ -222,6 +222,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
222 struct commit_info *commit_info =
223 get_commit_info(commit, &info->reflogs, 0);
224 struct commit_reflog *commit_reflog;
225 + struct object *logobj;
226 struct reflog_info *reflog;
227
228 info->last_commit_reflog = NULL;
@@ -233,15 +234,20 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
234 commit->parents = NULL;
235 return;
236 }
236 -
237 - reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
237 info->last_commit_reflog = commit_reflog;
239 - commit_reflog->recno--;
240 - commit_info->commit = (struct commit *)parse_object(reflog->osha1);
241 - if (!commit_info->commit) {
238 +
239 + do {
240 + reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
241 + commit_reflog->recno--;
242 + logobj = parse_object(reflog->osha1);
243 + } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
244 +
245 + if (!logobj || logobj->type != OBJ_COMMIT) {
246 + commit_info->commit = NULL;
247 commit->parents = NULL;
248 return;
249 }
250 + commit_info->commit = (struct commit *)logobj;
251
252 commit->parents = xcalloc(1, sizeof(struct commit_list));
253 commit->parents->item = commit_info->commit;
t/t1410-reflog.sh
+13
@@ -325,4 +325,17 @@ test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
325 test_cmp expect actual
326 '
327
328 +test_expect_success 'no segfaults for reflog containing non-commit sha1s' '
329 + git update-ref --create-reflog -m "Creating ref" \
330 + refs/tests/tree-in-reflog HEAD &&
331 + git update-ref -m "Forcing tree" refs/tests/tree-in-reflog HEAD^{tree} &&
332 + git update-ref -m "Restoring to commit" refs/tests/tree-in-reflog HEAD &&
333 + git reflog refs/tests/tree-in-reflog
334 +'
335 +
336 +test_expect_failure 'reflog with non-commit entries displays all entries' '
337 + git reflog refs/tests/tree-in-reflog >actual &&
338 + test_line_count = 3 actual
339 +'
340 +
341 test_done