회사에서 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를 저로 해서 반영했습니다. (수정내역)
Submit Comment
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/