4 minute read

모든게 예상대로 였다. 이렇게 오래 걸리리라는 것 빼고는. 누구나 인정할만한 중요한 문제였고, 수많은 기술들이 제시되었지만 대부분 한계가 있었고 여전히 실용성과는 거리가 멀었다. 분명 제대로 풀 수 있는 방법이 있으리라고 믿었다. 그리고 오랜 기간 공을 들였다. 그리고는 지난 주 CCS 2022 에 Tracer: Signature-based Static Analysis for Detecting Recurring Vulnerabilities 라는 논문을 발표한 것으로 첫번째 단계는 마무리 되었다.

이 이야기는 꽤 오래전으로 거슬러 올라간다. UPenn 에서 연구원으로 있던 시절이었다. 당시 주고 받았던 관련 메일을 찾아보니 2018년 1월에 처음 이와 관련된 대화가 나온다. 2017년 7월에 미국으로 넘어가자마자 당시 연구실에서 이미 진행되고 있던 연구에 참여하였고, 이를 11월 PLDI에 제출하고 난 이후였다. 정신없이 그 일을 마무리한 후, 이제 본격적으로 나만의 새로운 연구 주제를 찾기 위해 독을 품고 혈안이 되어있을 때였다.

정적 분석기의 실용성을 극대화하는 방법에 대해서 고민하던 중 한 오류 예제를 보게 됐다. 당시 지도교수가 다른 동료에게서 전해들은 이야기라며 보여준 오류 코드인데, 비슷한 오류 패턴이 계속 반복된다는 취지였다. 여러 프로그램에서 공통적으로 등장하는 비슷한 정수 오버플로우 오류 코드 묶음이었다. 4년 후, 바로 지난주에 발표한 논문에 예제로 쓰인 바로 그 오류 코드와 처음 만난 순간이었다.

인간은 비슷한 실수를 반복한다는 만고의 진리를 정조준하는 정적 분석기는 매우 흥미로웠다. 이를 당시에는 “패턴 기반 정적 분석기”라고 불렀다. 이러한 문제에 관해 이야기 할 때마다 모든 사람들이 그 중요성에 동의했다. 이미 존재하는 기술들은 프로그램의 구문의 유사성만을 비교하는 것들이 대부분이라 변종에 취약했다. 변종에 강하기 위해서는 전문가의 손길이 필요했다. 우리는 전문가들이 많은 노력을 들이지 않고도 손쉽게 패턴을 학습하고 널리 적용할 수 있는 분석기를 원했다. 그리고 정적 분석을 오랫동안 연구했고, 여러 학습 기술에도 경험이 많은 우리팀이 이 문제를 풀기에 제격이라 확신했다.

하지만 그 이후로는 길고 어두운 터널이었다. 상당히 많은 방식을 시도해 보았지만 모두 실패했다. 대부분은 아이디어 구상 수준에서 기각된 것이지만, 그 중에는 구현하고 실험까지 해본 것들도 많았다. 베이지안 네트워크나 확률적 오토마타로 패턴을 기술해 보려고 했지만 터무니 없었다. 프로그램 변환과 합성 기술을 이용하는 아이디어를 내보기도 했다. 나도 동료들도 잘 될것이라 확신했었고, 몇날 며칠 밤을 새워 구현하고 실험을 하였다. 허나, 초보적인 수준을 벗어나지 못했다.

이 정도 되니, 우리가 가진 예제가 너무 적어 사고의 폭이 제한된 것이 아닌가 싶은 생각이 들었다. 마침 그 당시에 알게 된 것이 Google 의 OSS-Fuzz 프로젝트인데, 자체 퍼징을 통해 찾은 수많은 실제 오류 데이터를 보유하고 있었다. 이 데이터만 다 확보한다면 분명 길이 보이리라 생각했다. 당장 뛰어들어 데이터를 모으기 시작했다. 겉보기에 데이터가 잘 정리되어있는 것처럼 보여서 쉽게 끝날 줄 알았는데, 역시나였다. 제 3자에게는 공개되지 않은 정보들이 많이 있었고, 이를 알아내기 위해서는 어마어마한 작업이 필요했다. 공개된 데이터를 파싱하고, 해당 프로젝트를 빌드하고, 퍼저가 찾은 입력을 넣고 오류를 재현해보는 전체 과정에서 수많은 문제가 속출했다. 동시에 이렇게 오류를 재현한 이후 패턴을 뽑기 위한 도구도 만들고 있었다. 오류 입력을 넣고 실행할 때 거쳐가는 실제 오류 경로 (trace!) 를 뽑아내어 패턴으로 기록하기 위해서 LLVM 컴파일러를 뜯어 고쳤다. 또한, 그 긴 경로에서 오류의 핵심만을 추출하기 위해서 기호실행 (symbolic execution) 에 버금가는 도구들도 구현했다.

처참한 실패였다. 이런 도구를 만드는데만 수개월을 쏟았지만, 날마다 새로운 문제가 쏟아져 나왔다. 결국 우리는 거기서 그만하기로 했다.

한국에 돌아온 이후에도 그 문제는 가끔 생각이 났다. 하지만, 진행하고 있는 다른 일이 있었고, 연구실에 인력도 부족한 상황이었기에 마음뿐이었다. 딱히 뾰족한 아이디어가 있는 것도 아니었다.

그러던 어느날 문득 Infer 의 경로 추출 기능이 위 모든 자질구레한 문제를 한 방에 해결할 수 있는 것이라는 생각이 들었다. 항상 좋은 생각이 떠오르는 출근길 기계공학동 앞 잔디밭이었던 것 같다 (착각인지도 모르겠다). 당시 Infer 를 이용하여 C++ 프로그램의 API 오사용 분석기를 만들고 있었다. 그때 매일 들여다보던 Infer 와 잠시 접어둔 패턴 기반 정적 분석기가 무의식에서 만난것 같다. 그때부터 Infer 에다가 현재 Trace 의 기반이 되는 분석기를 구현하기 시작했다. “add integer overflow checker” 라는 로그가 달린 첫 커밋을 보니 2020년 9월 20일이다. 근 1년만에 다시 집어든 칼이다.

당시 석사 신입생으로 들어온 우석이와 함께 연구를 시작했다. Infer 를 이용할 생각을 하니, 갑자기 모든 계획이 저절로 머릿속에 그려졌다. 오류 경로를 추출하는 것도 용이했고, 그렇게 추출된 오류 경로끼리 비교하는 것도 몇가지 알고리즘을 떠올릴 수 있었다. 그때부터 아주 뜨거운 1년이 시작되었다. 밤낮으로 대규모 실험이 진행되었고 구현을 고쳐야할 것도 많았다. 처음에는 내가 주로 분석기를 구현하고 우석이가 성능 테스트를 했지만, 몇 개월후부터는 점차 우석이가 분석기를 구현하는 비중이 늘어나고, 6개월쯤 뒤부터는 전적으로 실험은 우석이에게 맡겼다. 구현은 안정화 되어갔고, 회의 때 우석이가 실제 오류를 발견했다며 가져오는 사례들도 점점 늘어갔다.

딱 1년뒤 2021년 9월 ICSE에 도전했다. 결과는 실패였다. 아이디어는 좋고 문제가 재미있으나 기존 기술과 비교가 제대로 안되었고, 성능 평가가 부실하다는 지적이었다. 일견 맞는 지적이었다. 기존 기술과 돌아가는 방식이 달라서 1:1로 비교하기가 어려운 나머지 실험결과에 대한 명쾌한 설명을 하지 못했다. 다시 한번 뼈저리게 느꼈다. 이러한 상황에서도 비교할 수 있는 합당한 상황을 만들어 내는 것이 연구자의 능력이고, 동시에 의무이다.

이 긴 여정은 그 이후 다른 학회에 한 번 더 떨어진 이후, 그 다음 번 도전에서 끝이 났다. 놀랍지는 않았다. 언젠가 반드시 좋은 평가를 받을 것이라 확신했고 단지 시간 문제라 생각했다. 그 긴 여정이 코로나 봉쇄시기에도 꾸준히 이어진 덕택에 오랜만에 직접 학회를 방문할 기회도 얻은 것도 큰 행운이다.

이 여정은 늘 내가 굳게 믿고 있는 아래와 같은 신념을 다시금 확인하게 한다.

  • 좋은 연구는 좋은 문제에서 출발한다. 좋은 기술이 아니라: Tracer 가 사용한 기술은 그다지 심오하지 않다. 오히려 매우 단순하다. 하지만, 우리가 대상으로 한 문제는 중요했고, 동기는 확실했다. 긴 실패 여정속에서 만난 한 심사평에는 이렇게 적혀있었다: “딥러닝같은 과한 기술을 사용하지 않고 가벼운 기술을 사용하여 문제를 해결한 점이 흥미롭다”
  • 좋은 연구는 문제의 핵심을 드러내는 구체적인 예제에서 출발한다: 2018년 1월에 만난 그 예제는 그 이후 수년동안 우리와 함께했고 결국 논문의 제일 처음 예제로 들어갔다. 이 예제를 통해서 문제를 제대로 이해할 수 있었고, 해결책을 위한 여러 아이디어를 떠올릴 수 있었다.
  • 좋은 연구자는 안되는 것 중에서 되는 부분을 찾아낼 수 있어야 한다: 실험 평가를 하다보면 늘 겪는 일이다. 우리 기술과 정확히 1:1 대응되는 기존 기술은 흔치 않다. 하지만 많은 경우 독자들은 특정 한가지 측면에서라도 비교한 결과를 보고 싶어하며, 그러한 매력적인 측면과 결과를 만들어 내는 것은 실험 과학자로서 중요한 능력이다.

이 연구에 관해 더 자세한 기술적인 내용은 Tracer 홈페이지를 참고하시라.