콘텐츠로 이동

cargo-fuzz - libFuzzer로 Rust 퍼징 치트시트

cargo-fuzz - libFuzzer로 Rust 퍼징 치트시트

cargo-fuzz는 Rust 코드를 퍼징하는 표준 방법입니다. 이는 libFuzzer를 Cargo 원어민 워크플로우로 연결합니다: fuzz 대상을 임의 바이트를 취하는 일반적인 Rust 함수로 작성하고, cargo fuzz run을 실행하고, 커버리지 기반 퍼징은 panic, 산술 오버플로우, — unsafe 코드에서 — 진정한 메모리 오류를 사냥합니다. 안전한 Rust는 메모리 손상을 방지하지만 panic, 무한 루프, 또는 논리 버그를 방지하지 않으며 모든 unsafe 블록 또는 C 종속성이 메모리 문제의 전체 클래스를 재도입합니다.

설치

단계명령어
설치cargo install cargo-fuzz
Nightly 필요rustup install nightly (libFuzzer는 nightly 필요)
초기화cargo fuzz init (크레이트에서)
확인cargo fuzz --version

Fuzz 대상 생성

cargo fuzz init                 # fuzz/ 디렉터리 생성
cargo fuzz add parse_input      # 새 대상 생성
// fuzz/fuzz_targets/parse_input.rs
#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    if let Ok(s) = std::str::from_utf8(data) {
        let _ = my_crate::parse(s);   // 여기 panic은 발견됨
    }
});

실행

명령어설명
cargo fuzz run parse_input대상을 퍼징
cargo fuzz run parse_input -- -max_total_time=3005분 실행
cargo fuzz run parse_input -j 88개 병렬 작업
cargo fuzz list대상 나열
cargo fuzz build실행하지 않고 빌드

임의로 구조화된 입력

원본 바이트는 대상이 즉시 거부하는 입력에 시간을 낭비합니다. arbitrary 크레이트는 대신 타입화된 값을 구축합니다:

use arbitrary::Arbitrary;

#[derive(Arbitrary, Debug)]
struct Config { name: String, retries: u8, enabled: bool }

fuzz_target!(|cfg: Config| {
    let _ = my_crate::apply_config(cfg);
});
이점이유
유효한 모양 입력더 심층 논리에 도달
타입화된 대상파서가 아닌 API 퍼징
Debug 출력읽을 수 있는 재현

살균기

살균기플래그
AddressSanitizer (기본값)-s address
LeakSanitizerASan에 포함됨
MemorySanitizer-s memory
ThreadSanitizer-s thread
없음 (빠름)-s none
# ASan으로 unsafe FFI 코드 퍼징
cargo fuzz run ffi_target -s address

코퍼스 및 커버리지

명령어목적
fuzz/corpus/<target>/흥미로운 입력이 축적되는 곳
cargo fuzz cmin <target>코퍼스 최소화
cargo fuzz tmin <target> <input>충돌 입력 최소화
cargo fuzz coverage <target>커버리지 데이터 생성
시드 코퍼스부트스트랩하기 위해 유효한 예제 추가

시드 코퍼스를 실제 유효한 입력으로 채우는 것이 퍼징을 빠르게 생산적으로 만드는 가장 효과적인 단일 방법입니다.

충돌 재현

# 충돌은 fuzz/artifacts/<target>/에 착지합니다
cargo fuzz run parse_input fuzz/artifacts/parse_input/crash-abc123

# 최소한의 경우로 축소
cargo fuzz tmin parse_input fuzz/artifacts/parse_input/crash-abc123

최소화된 입력을 일반적인 #[test]로 변환하여 버그가 수정된 상태로 유지되도록 합니다.

Rust에서 퍼징할 것

좋은 대상이유
파서 / deserializer신뢰할 수 없는 입력, 복잡한 상태
unsafe 블록메모리 안전이 실제로 위험함
FFI 경계인터페이스 뒤의 C 코드
프로토콜/상태 머신순서 버그
압축 / 인코딩고전적인 버그 영역
덜 유용이유
순수 안전 산술Panic은 보통 명백함
얇은 래퍼탐색할 논리가 적음

cargo-fuzz vs 다른 퍼저

측면cargo-fuzzAFL++ (afl.rs)LibAFLhonggfuzz-rs
엔진libFuzzerAFL++사용자 정의honggfuzz
설정Rust에 가장 쉬움중간직접 구축쉬움
구조화된 입력arbitraryarbitrary사용자 정의 타입arbitrary
최적 대상표준 Rust 퍼징영구/바이너리 모드이상한 대상하드웨어 피드백

비 Rust 대상은 AFL++ 또는 honggfuzz를 참조합니다; 사용자 정의 퍼저는 LibAFL을 참조합니다.

리소스