Posted by on the 4th of March, 2016 at 1:17 am under 컴퓨터.  This post has no comments.

회사에서 scm툴로 git을 쓰는데 repository 정보를 보기위해 gitg를 씁니다.

gitg가 처음에는 0.xx 버전으로 시작하다가 어느순간 gnome으로 편입되면서 버전도 올라가고 인터페이스도 많이 바뀌었습니다.

제 입장에서는 예전 0.xx 버전일때가 더 쓰기 편했습니다. 그래도 gtk3도 적용되고 gnome3 인터페이스도 나름 괜찮아서 최근 버전을 쓰고 있습니다.

문제는 gitg가 심심하면 crash가 납니다. 이건 예전 0.xx 버전때도 그랬고 얼마전까지 쓴 3.16 때도 그랬어요. 그나마 하루에 많아야 한두번이라 다시 실행하고는 말았습니다.

그런데 며칠 전 3.18 버전으로 올렸더니 무조건 crash가 발생하네요. 이대로는 전혀 쓸 수 없어서 버그 리포트를 올리고 직접 원인도 찾아보았습니다.

gdb로 callstack을 확인해보니 null pointer dereference 때문에 segmentation fault가 발생했습니다.

문제가 되는 함수 구현부를 확인 해봤습니다.

GDateTime *
ggit_signature_get_time (GgitSignature *signature)
{
 git_signature *s;
 GDateTime *utc;
 GTimeZone *tz;
 GDateTime *ret;

 g_return_val_if_fail (GGIT_IS_SIGNATURE (signature), NULL);

 s = _ggit_native_get (signature);

 utc = g_date_time_new_from_unix_utc (s->when.time);
 tz = ggit_signature_get_time_zone (signature);
 ret = g_date_time_to_timezone (utc, tz);
 g_date_time_unref (utc);

 return ret;
}

g_date_time_new_from_unix_utc 함수가 NULL를 return하고 있는데 알고보니 인자로 전달한 s->when.time의 timestamp 값이 이상한 값이었습니다.

메모리가 깨진것으로 의심되어 valgrind를 돌려보았습니다. 역시나 invalid read가 검출됩니다.

	public RefRow(Gitg.Ref? reference, RefAnimation animation = RefAnimation.NONE)
	{
		this.reference = reference;

		if (reference != null)
		{
			try
			{
				var obj = reference.resolve().lookup();

				if (obj is Ggit.Tag)
				{
					d_updated = ((Ggit.Tag)obj).get_tagger();
				}
				else if (obj is Ggit.Commit)
				{
					d_updated = ((Ggit.Commit)obj).get_committer();
				}
			}
			catch (Error e)
			{
				stderr.printf("%s\n", e.message);
			}
		}

위 코드에서 try 블록의 obj가 생성되었다가 try 블록 끝나는 시점에 사라집니다. 그런데 중간에 get_tagger나 get_committer를 받아 d_updated에 저장해서 나중에 사용하고 있습니다. 물론 이미 try 블록이 끝나면서 메모리 영역이 해제되기 때문에 이상한 값이 들어가 crash가 발생했습니다.

해결 방법은 d_updated가 obj에 대해서 reference 하는 방법과 get_tagger, get_commiter 함수로 받아오는 object 자체에 data를 복사하는 방법이 있습니다. 두 방법 중 두번째가 수정하기 간단했습니다.

--- libgit2-glib/ggit-commit.c.orig	2016-02-23 13:29:46.992498260 +0900
+++ libgit2-glib/ggit-commit.c	2016-02-23 13:29:51.288498259 +0900
@@ -224,16 +224,18 @@
 ggit_commit_get_committer (GgitCommit *commit)
 {
 	git_commit *c;
-	const git_signature *signature;
+	const git_signature *committer;
+	git_signature *signature;
 
 	g_return_val_if_fail (GGIT_IS_COMMIT (commit), NULL);
 
 	c = _ggit_native_get (commit);
-	signature = git_commit_committer (c);
+	committer = git_commit_committer (c);
+	git_signature_dup(&signature, committer);
 
-	return _ggit_signature_wrap ((git_signature *)signature,
+	return _ggit_signature_wrap (signature,
 	                             ggit_commit_get_message_encoding (commit),
-	                             FALSE);
+	                             TRUE);
 }
 
 /**

위와 같이 고치니 crash는 더이상 발생하지 않았습니다.

위 내용을 gnome bugzilla에 올렸고 (Bug 762427) maintainer가 고맙게도 author를 저로 해서 반영했습니다. (수정내역)



* Required

The URI to TrackBack this entry is:
https://bmp.pe.kr/blog/index.php/2016/03/04/gitg-%eb%b2%84%ea%b7%b8-%eb%a6%ac%ed%8f%ac%ed%8a%b8/trackback/