Lobsters

Fuzzers vs. AI: Comparing Fuzzilli and BigSleep on JavaScript Engines

퍼저와 AI: JavaScript 엔진에서 Fuzzilli와 BigSleep 비교

Google Project Zero는 V8에서 퍼저 Fuzzilli와 LLM 기반 버그 탐색 시스템 BigSleep의 성과를 비교했습니다. 최근 6개월간 두 도구가 찾은 취약점은 45건과 37건이고, 실제 운영 데이터의 중복은 약 25%였습니다. 과거 버그를 재발견하는 실험에서는 중복률이 60% 안팎으로 나타나 두 접근법을 함께 운영해야 한다고 분석합니다.

에디터 노트

운영 데이터에서 Fuzzilli와 BigSleep이 찾은 취약점 수는 37건과 45건으로 비슷하고, 두 도구가 함께 찾은 비율은 약 25%입니다. 과거 버그를 대상으로 한 실험에서는 BigSleep이 원래 취약점의 약 62%를 재발견했지만, 표준 x64 환경과 제한된 플래그에서 재현되는 112건만 평가했고 커밋을 시작점으로 삼았습니다. 따라서 두 수치의 차이는 도구 성능만이 아니라 실험 조건과 운영 중 수정으로 생긴 발견 기회 차이도 반영합니다. 사례는 서로 다른 강점을 구체적으로 보여줍니다. BigSleep은 레이블 분기와 switch의 초기화 상태 병합을 연결해 TDZ 버그를 추론했고, Fuzzilli는 개별 최적화만 봐서는 놓치기 쉬운 Maglev 단계 간 표현 충돌을 찾아냈습니다. 어느 한쪽이 다른 쪽을 대체한다는 결론보다 병행 운영이 현재 데이터에 더 잘 맞습니다. 댓글의 테스트 경험담은 에이전트 리뷰에 테스트 생성까지 더하면 발견 폭이 넓어졌다는 주장이고, 적용 언어나 대상은 답글에서 질문만 나와 확인되지 않았습니다. 또 다른 댓글은 글의 관리 측면을 언급하지만 구체적인 기술 반론은 제시하지 않습니다.

AI 요약

Google Project Zero는 Chrome의 JavaScript 엔진 V8에서 실제 운영 중인 퍼저 Fuzzilli와 LLM 기반 분석 에이전트 BigSleep을 비교합니다. 두 도구 모두 새 코드에서 버그를 자동으로 찾아 Clusterfuzz를 거쳐 Chromium 버그 트래커에 보고합니다. Fuzzilli는 여러 인스턴스에서 지속적으로 실행되고, BigSleep은 하루 한 번 새 커밋을 검토해 버그가 의심되면 재현용 테스트 케이스를 작성합니다. 비교 대상은 V8 하나이며, 저자들은 이 결과를 다른 프로젝트나 도구 전체로 일반화하지 않습니다.

운영 환경의 발견 건수

2026년 4월부터 9월까지 전체 버그 보고는 Fuzzilli 316건, BigSleep 441건입니다. 취약점 유형만 보면 Fuzzilli 178건, BigSleep 95건으로 순위가 바뀝니다. 하지만 중복, 개발자 조치 없이 닫힌 보고, WAI(의도된 동작) 판정 등을 고려해야 합니다. 저자들은 조치 가능한 취약점을 중복이나 WAI로 분류하지 않고 수정 커밋이 연결된 버그로 정의합니다. 이 기준에서는 BigSleep 45건, Fuzzilli 37건으로 비슷한 수준입니다. 두 도구의 비용과 취약점 한 건당 비용은 아직 비교하기 어렵다고 설명합니다.

발견 결과의 중복

버그 트래커에서 중복으로 연결된 보고만 찾지 않고, Clusterfuzz 단계에서 걸러진 중복도 데이터베이스를 조회해 반영했습니다. 조치 가능한 취약점 81건 가운데 두 도구가 모두 찾은 사례는 20건으로 약 25%입니다. 별도 실험에서는 BigSleep에게 과거 Fuzzilli 버그를 다시 찾게 했습니다. 2023년 초부터 2026년 중반까지의 보고 약 1,150건에서 취약점 약 600건을 골랐고, x64 debug+asan 환경에서 표준 플래그로 재현되는 버그를 추려 최종 112건을 평가했습니다. 버그를 일으킨 것으로 추정되는 커밋을 검토 과제로 만들고, 각 커밋마다 최대 25회 시도하게 했습니다. 재현 테스트가 수정 전 버전에서는 실패하고 수정 후 버전에서는 통과하는지 확인해 올바른 버그를 찾았는지 판별했습니다.

이 실험에서 BigSleep은 원래 버그의 약 62%를 재발견했습니다. 약 12%에서는 해당 커밋과 관련된 다른 버그를 찾았고, 약 26%에서는 버그를 찾지 못했습니다. 시작점을 취약한 파일이나 함수로 바꾸거나, 메모리 손상 취약점뿐 아니라 assertion 실패도 찾도록 프롬프트를 바꾼 추가 실험에서는 미발견 버그 10건을 더 찾았습니다. 전체 재발견 비율은 약 70%, 어떤 버그든 찾은 비율은 약 84%가 됐습니다. 다만 추가 실험은 버그마다 5회만 시도했고, 퍼저에도 비슷한 조정을 했다면 성과가 달라질 수 있다고 저자들은 덧붙입니다. 모델 학습 데이터의 암기를 살펴본 분석에서는 2026년 이전 버그에서 통계적으로 유의한 암기 효과를 확인하지 못했습니다.

도구별로 달랐던 버그 유형

BigSleep이 찾아낸 사례 중 하나는 V8의 TDZ(Temporal Dead Zone) 검사 제거 최적화 버그입니다. 기존 수정은 do-while 루프에 break가 있을 때 루프 조건에서 변수 초기화 상태가 잘못 기록되지 않도록 했습니다. 에이전트는 레이블이 붙은 break로 바깥 switch를 빠져나가면 안쪽 루프가 break를 인식하지 못할 가능성을 추론했습니다. switch의 분기별 초기화 상태를 합치는 동작까지 이용한 테스트 케이스를 작성해, 초기화 전 변수의 내부 sentinel 값인 the_hole이 JavaScript에 노출되는 경로를 보였습니다.

다른 사례는 예외 처리용 바이트코드 핸들러 오프셋의 비트 폭 문제입니다. 에이전트는 필드가 28비트여서 약 256MB까지만 표현하지만 최대 바이트코드 크기는 512MB라는 점을 찾았습니다. 256MB를 넘는 큰 함수와 예외 처리기를 만들어 잘못된 바이트코드 위치로 이동하는 충돌을 재현했습니다. 이 사례는 큰 테스트 입력을 느리게 실행하는 일이 일반적인 커버리지 기반 퍼저의 시간 제한과 맞지 않을 수 있음을 보여줍니다.

반대로 Fuzzilli가 찾고 BigSleep이 재발견하지 못한 사례에서는 Maglev 컴파일러의 두 최적화가 충돌했습니다. 그래프 빌더는 값이 Smi 정수라고 가정해 검사를 생략했지만, 표현 선택 단계가 Phi 노드의 표현을 바꾸면서 값이 HeapNumber로 바뀌는 경로를 만들었습니다. 그 결과 포인터를 Smi처럼 해석해 메모리 손상으로 이어졌습니다. 각 최적화만 따로 보면 타당해 보여 소스 코드 일부만 검토해서는 놓치기 쉽고, 실행 중 값의 변화와 여러 컴파일 단계의 상호작용을 퍼저가 포착했습니다.

저자들의 해석

퍼저는 실행 결과와 커버리지 피드백을 이용해 소스만 읽어 찾기 어려운 동작을 발견하고, 에이전트는 복잡한 조건 조합이나 특정 경계값, 새로운 언어 기능, 특수 실행 환경이 필요한 버그를 추론하는 데 강점을 보일 수 있습니다. 반면 퍼저가 찾기 어려운 버그는 에이전트도 찾기 어려운 경향이 있었고, JIT 컴파일러 버그에서는 두 도구의 중복이 더 낮았습니다. 과거 버그 재발견 실험의 중복률은 운영 데이터보다 높지만, 실험 대상이 표준 x64 환경에서 재현되는 일부 버그로 제한됐고 운영에서는 수정이 먼저 이뤄지면 다른 도구가 재발견할 기회를 잃습니다. 저자들은 현재 두 접근법이 상호 보완적이며, 에이전트의 모델과 실행 환경이 개선되면 향후 성과 비중이 달라질 수 있다고 봅니다.

Lobsters 반응

  • @Student — 흥미롭습니다. 제 작업에서는 에이전트 코드 리뷰도 괜찮았지만, 에이전트를 이용해 생성형 테스트와 결정론적 테스트를 추가하니 더 많은 버그와 더 흥미로운 버그를 찾았습니다.
    • @glacambre — 흥미롭네요. 어떤 기법을 어떤 맥락에서 쓰는지, 예를 들어 언어와 테스트 대상 소프트웨어 종류를 더 설명해 주실 수 있나요?
  • @riking — 이 블로그 글은 관리 측면의 이야기가 놀라울 만큼 깊습니다.

원문: Google Project Zero / 번역·요약: Trawling