결과적으로 형식 검증은 강력하지만, 신뢰 기반 컴퓨팅 베이스(TCB) 의 안전성 확보 없이는 전체 시스템의 안정성을 보장할 수 없음
Lean 검증을 통과한 프로그램에서 발견된 버그
Lean으로 형식 검증된 zlib 구현체를 퍼징한 결과, Lean 4 런타임에서 힙 버퍼 오버플로우가 발견됨
검증된 애플리케이션 코드에는 메모리 취약점이 없었음
그러나 Lean 런타임의 lean_alloc_sarray 함수에서 오버플로우가 발생해 모든 Lean 4 버전에 영향을 미침
AI 기반 퍼저 Claude와 AFL++, AddressSanitizer, Valgrind, UBSan 등을 이용해 1억 회 이상의 퍼징을 수행
lean-zip의 압축·해제·아카이브 처리 등 6개 공격면을 대상으로 16개의 병렬 퍼저를 구동
19시간 동안 4개의 크래시 입력과 1개의 메모리 취약점이 발견됨
두 가지 주요 버그가 확인됨
Lean 런타임의 lean_alloc_sarray에서 힙 버퍼 오버플로우
lean-zip의 Archive.lean 모듈에서 Out-of-Memory 기반 서비스 거부(DoS)
형식 검증의 한계가 드러남
lean-zip의 압축·해제 알고리듬은 완전 검증되었으나, 아카이브 파서(Archive.lean)는 검증되지 않아 DoS 취약점이 존재
런타임 버그는 신뢰 기반 컴퓨팅 베이스(Trusted Computing Base) 내부 문제로, 모든 Lean 프로그램에 영향을 미침
결론적으로, Lean의 형식 검증은 애플리케이션 코드의 안정성을 입증했지만, 검증 범위 밖의 구성요소에서는 여전히 취약점이 존재함
검증은 적용된 범위 내에서만 강력하며, 기초 신뢰 계층의 안전성 확보가 필수적임
퍼징 실험 개요
대상 코드베이스는 lean-zip의 검증된 zlib 구현체
모든 정리(theorem)와 명세(specification), 문서, C FFI 바인딩을 제거해 순수 Lean 코드만 남김
Claude가 코드가 검증된 사실을 인지하지 못하도록 하여 편향을 방지
실험 환경
16개의 병렬 퍼저가 ZIP, gzip, DEFLATE, tar, tar.gz, compression 등 6개 공격면을 대상으로 실행
AddressSanitizer, UndefinedBehaviorSanitizer, Valgrind memcheck, cppcheck, flawfinder 등을 병행
48개의 수작업 익스플로잇 파일을 포함해 총 105,823,818회 실행, 359개의 시드 파일 사용
19시간 동안 4개의 크래시 입력과 1개의 메모리 취약점이 발견됨
버그 1: Lean 런타임의 힙 버퍼 오버플로우
취약 함수: lean_alloc_sarray
모든 ByteArray, FloatArray 등의 스칼라 배열을 할당하는 함수
sizeof(lean_sarray_object) + elem_size * capacity 계산 시 정수 오버플로우 발생 가능
문제 원인
capacity가 SIZE_MAX에 근접할 경우 덧셈이 오버플로우되어 작은 버퍼가 할당됨
이후 호출자가 n 바이트를 읽어들여 힙 버퍼 오버플로우 발생
트리거 조건
lean_io_prim_handle_read에서 nbytes가 매우 큰 값일 때 발생
ZIP64 헤더의 compressedSize가 0xFFFFFFFFFFFFFFFF인 156바이트 파일로 재현 가능
Lean 4의 모든 버전(최신 nightly 포함)에 영향
재현 코드 예시
def main : IO Unit := do
IO.FS.writeFile "test.bin" "hello"
let h ← IO.FS.Handle.mk "test.bin" .read
let n : USize := (0 : USize) - (1 : USize)
let _ ← h.read n