Using Nix to Fuzz Test a PDF Parser (Part One)

Michael Lynch

Nix로 PDF 파서 퍼즈 테스트하기 (1부)

퍼즈 테스팅은 소프트웨어의 버그를 자동으로 찾아내는 기법입니다. 문제는 설정이 너무 번거롭다는 점입니다. 퍼즈 테스팅 튜토리얼을 하나 읽어 보면, 첫 번째 과제는 늘 소스에서 도구를 빌드하고 꼬리에 꼬리를 무는 의존성을 쫓아다니는 데 한 시간을 쓰는 일입니다.

최근 저는 Nix가 퍼즈 테스팅의 단순 반복 작업을 상당 부분 없애준다는 사실을 알게 되었습니다. 단일 명령어로 퍼즈 테스팅 워크플로를 시작하는 Nix 설정을 만들었습니다. 필요한 것은 Nix와 git뿐입니다.

Nix와 퍼즈 테스팅 모두 초보자이지만, 이 Nix 워크플로를 이용해 PDF 렌더러에서 아직 패치되지 않은 버그를 찾아냈습니다.

해결책 미리 보기

최종 결과물을 미리 보여드리겠습니다. 다음 한 줄 명령어로 오픈소스 PDF 리더의 퍼즈 테스팅을 시작할 수 있습니다.

nix run gitlab:mtlynch/fuzz-xpdf

이 명령어는 Nix가 설치된 모든 Linux 시스템에서 동작해야 하며, 아마도 macOS에서도 동작할 것입니다. 몇 분 정도 빌드가 진행된 뒤에는 다음과 같은 터미널 UI가 나타납니다.

honggfuzz 터미널 UI 스크린샷, pdftotext 퍼즈 테스팅 진행 상황 표시

Nix 덕분에 모든 의존성을 설치하고 단일 명령어로 퍼즈 테스팅을 시작할 수 있습니다.

위 명령어를 실행하면 다음과 같은 일이 차례로 일어납니다.

  1. Nix가 PDF 리더와 테스트 툴체인에 필요한 모든 도구와 의존성을 다운로드합니다.
  2. Nix가 퍼즈 테스팅에 필요한 계측(instrumentation)이 적용되도록 PDF 리더를 소스에서 컴파일합니다.
  3. Nix가 테스트 입력 생성을 위한 엣지 케이스 PDF 모음을 다운로드합니다.
  4. Nix가 자동으로 새로운 PDF를 생성해 PDF 리더에 투입하고, 어떤 입력이 크래시를 유발했는지 보고합니다.

퍼징 옵션을 바꾸거나 다른 버전의 PDF 리더를 테스트하고 싶다면, 파일 하나만 수정하면 됩니다.

이 글에서는 퍼즈 테스팅 워크플로를 단계별로 어떻게 만들었는지 공유하겠습니다. 같은 방법을 다른 프로젝트에서 버그를 찾는 데에도 활용할 수 있습니다.

빨리 결과를 보고 싶다면 맨 끝으로 건너뛰어 최종 결과물을 확인하셔도 됩니다.

퍼즈 테스팅이란?

퍼즈 테스팅, 즉 ‘퍼징’은 입력 데이터를 무작위로 생성한 뒤 그 입력이 대상 애플리케이션을 크래시시키는지 확인해 소프트웨어의 버그를 찾는 방법입니다.

예를 들어 JPEG 이미지를 리사이징하는 프로그램을 테스트한다면 워크플로는 다음과 같이 진행됩니다.

  1. 유효한 JPEG 파일 및/또는 손상된 JPEG 파일 모음을 준비합니다.
  2. 입력 파일 중 하나를 무작위로 선택해 무작위로 변형합니다(일부 비트 뒤집기, 데이터 추가, 데이터 삭제 등).
  3. 변형된 입력 파일을 이미지 리사이징 프로그램에 넣습니다.
  4. 변형된 입력 때문에 프로그램이 크래시되거나 멈췄다면 나중에 분석할 수 있도록 해당 입력을 저장합니다.
  5. 2단계로 돌아가 반복합니다.

Nix란?

Nix는 매우 다양한 일을 하는 복잡한 도구이며, 저 역시 그중 상당수는 아직 제대로 이해하지 못했습니다.

이 글에서는 Nix에 대해 다음 두 가지만 이해하면 충분합니다.

  • Nix는 패키지 매니저입니다. aptyum과 비슷하며, Nix 환경 안에서 실행할 수 있는 10만 개 이상의 패키지를 제공합니다.
  • Nix는 빌드 도구입니다. makeDocker와 비슷하며, 일련의 빌드 단계와 그 단계 간의 의존성을 정의할 수 있게 해줍니다. Nix에 빌드를 요청하면 요청한 결과를 만들기 위해 필요한 모든 단계를 수행합니다.

필요한 것

따라 하려면 다음 두 가지만 있으면 됩니다.

퍼징 대상 선정

제가 퍼즈 테스팅하는 PDF 리더는 xpdf라는 프로그램입니다. PDF 뷰어이지만 다양한 PDF 유틸리티 모음을 함께 제공합니다. 그중 pdftotext는 매우 단순하기 때문에 퍼징 대상으로 매력적입니다. GUI가 없고, PDF를 입력으로 받아 일반 텍스트를 출력할 뿐입니다. 그럼에도 xpdf의 복잡한 PDF 파싱 코드를 그대로 사용하므로, pdftotext에서 버그를 찾았다면 xpdf 전체에서도 버그를 찾았을 가능성이 높습니다.

Nix 기본 템플릿 만들기

프로젝트를 시작하기 위해 새 폴더와 git 저장소를 만듭니다.

mkdir fuzz-xpdf \
  && cd fuzz-xpdf \
  && git init

다음으로 flake.nix라는 파일을 만듭니다.

{
  description = "compile xpdf from source for fuzzing";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        packages = rec {
          default = xpdf;

          xpdf = pkgs.stdenv.mkDerivation rec {
            # TODO: I'll populate this next.
          };
        };
      }
    );
}

이것은 Nix 패키지와 애플리케이션 집합을 정의하는 Nix ‘flake’입니다.

지금까지는 단순한 Nix flake의 뼈대일 뿐입니다. 대부분은 자세히 설명할 필요가 없지만, 다음 한 줄만은 짚고 넘어가겠습니다.

nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";

이 줄은 패키지를 가져올 때 패키지 저장소의 2024년 5월 브랜치에서 가져오라는 의미이며, 이 글을 쓰는 시점 기준 최신 안정 브랜치입니다.

이 파일은 아직 뼈대일 뿐이라 빌드가 성공하지 않습니다. Nix로 xpdf를 컴파일하려면 몇 가지를 더 추가해야 합니다.

소스 tarball 지정하기

xpdf를 컴파일하려면 소스 코드 사본이 필요합니다.

먼저 mkDerivation을 호출합니다. 이는 Nix가 빌드 구성 요소를 정의하는 방법입니다. 패키지 이름(pname)과 버전이 필요하므로, 퍼징하려는 패키지인 xpdf와 이 글을 쓰는 시점 기준 xpdf의 최신 공개 버전인 4.05를 지정합니다.

{
  xpdf = pkgs.stdenv.mkDerivation rec {
    pname = "xpdf";
    version = "4.05";
    ...

mkDerivation의 다른 필수 필드는 src 속성으로, Nix가 빌드에 필요한 입력을 어떻게 가져올지 지정합니다. xpdf의 경우 소스 tarball은 다음 URL에 있습니다.

pnameversion 변수를 사용해 xpdf의 tarball URL을 지정합니다. 나중에 버전 번호가 바뀌어도 URL이 그대로 동작하도록 하기 위해서입니다.

{
  xpdf = pkgs.stdenv.mkDerivation rec {
    ...
    src = pkgs.fetchzip {
      url = "https://dl.xpdfreader.com/${pname}-${version}.tar.gz";
      extension = "tar.gz";
    };

문제는 Nix가 로컬 버전이 서버에 있는 버전과 일치하는지 확인하기 위해 tarball의 해시가 필요하다는 점입니다. 이 상태에서 nix build를 실행하면 Nix가 해시가 잘못되었다고 불평합니다.

warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
error: hash mismatch in fixed-output derivation '/nix/store/z3ckfdjqpfd73xkkwsnpg4ijwj60vyz8-source.drv':
         specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
            got:    sha256-LBxKSrXTdoulZDjPiyYMaJr63jFHHI+VCgVJx310i/w=

해시 불일치를 해결하기 위해 오류 메시지에서 “got” 옆에 나온 값을 flake.nix에 붙여 넣습니다.

{
  xpdf = pkgs.stdenv.mkDerivation rec {
    ...
    src = pkgs.fetchzip {
      url = "https://dl.xpdfreader.com/${pname}-${version}.tar.gz";
      # Paste the hash that appeared next to "got" in the error message.
      hash = "sha256-LBxKSrXTdoulZDjPiyYMaJr63jFHHI+VCgVJx310i/w=";
      extension = "tar.gz";
    };

소스에서 xpdf 컴파일하기

이제 Nix에게 xpdf 소스 코드를 가져오는 방법을 알려줬으니, 그 코드를 어떻게 빌드할지 알아봐야 합니다.

xpdf 컴파일 설명서에는 다음과 같은 의존성이 나열되어 있습니다.

Make sure you have the following installed:

  • CMake 2.8.8 or newer
  • FreeType 2.0.5 or newer
  • Qt 5.x or 6.x (for xpdf only)
  • libpng (for pdftopng and pdftohtml)
  • zlib (for pdftopng and pdftohtml)

저는 pdftotext만 실행하면 되므로 CMake와 FreeType만 있으면 됩니다.

복잡한 도구를 소스에서 빌드하는 과정은 보통 고통스럽습니다. 도구 A를 빌드하려는데 라이브러리 X에 의존해서 X를 어떻게 설치할지 알아봐야 하고, 알고 보니 라이브러리 X는 다시 라이브러리 Y와 Z에 의존해서 또 그것들을 어떻게 설치할지 알아봐야 하는 식입니다.

Nix는 소스 빌드 과정을 두 가지 측면에서 획기적으로 단순화합니다.

  • Nix는 어떤 패키지 매니저보다도 큰 패키지 저장소 중 하나를 가지고 있어, 필요한 대부분의 패키지가 이미 마련되어 있습니다.
  • Nix 패키지는 특정 OS 버전에 종속되지 않으므로, 제 아키텍처에 맞는 Nix 패키지만 있으면 바로 사용할 수 있습니다.

Nix 패키지 저장소를 살펴보니 실제로 CMake와 FreeType 패키지가 이미 제공되고 있었습니다.

CMake는 빌드 시에만 필요하고 실행 시에는 필요 없을 것이라 가정하므로 nativeBuildInputs에 넣습니다. FreeType은 실행 시에도 필요할 것 같으므로 buildInputs에 지정합니다.

{
  xpdf = pkgs.stdenv.mkDerivation rec {
    ...

    # Build dependencies belong here.
    nativeBuildInputs = with pkgs; [
      cmake
    ];

    # Runtime dependencies belong here.
    buildInputs = with pkgs; [
      freetype
    ];

이 시점에서 제 flake.nix는 다음과 같이 생겼습니다.

{
  description = "compile xpdf from source for fuzzing";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        packages = rec {
          default = xpdf;

          xpdf = pkgs.stdenv.mkDerivation rec {
            pname = "xpdf";
            version = "4.05";

            src = pkgs.fetchzip {
              url = "https://dl.xpdfreader.com/${pname}-${version}.tar.gz";
              hash = "sha256-LBxKSrXTdoulZDjPiyYMaJr63jFHHI+VCgVJx310i/w=";
              extension = "tar.gz";
            };

            nativeBuildInputs = with pkgs; [
              cmake
            ];

            buildInputs = with pkgs; [
              freetype
            ];
          };
        };
      }
    );
}

Nix로 빌드하면 result라는 폴더 아래에 결과물이 생성되므로, 해당 폴더를 소스 관리에서 제외하는 .gitignore 파일을 만듭니다.

echo 'result' > .gitignore

다음으로 모든 파일을 git 저장소에 추가합니다.

git add --all

참고: Nix flake의 짜증 나는 함정 중 하나는 파일이 git 소스 관리 아래에 있지 않으면 Nix가 그 파일을 인식하지 못한다는 점입니다. “file not found” 오류가 발생하면 파일을 git에 추가했는지 확인해 보세요.

마지막으로 nix build로 패키지를 소스에서 빌드합니다.

nix build

모든 것이 정상이라면 ./result/bin 아래에 실행 가능한 바이너리 모음이 있어야 합니다.

$ ls ./result/bin/
pdfdetach  pdffonts  pdfimages  pdfinfo  pdftohtml  pdftopng  pdftoppm  pdftops  pdftotext

실제로 pdftotext가 정상적으로 동작합니다.

$ ./result/bin/pdftotext -v
pdftotext version 4.05 [www.xpdfreader.com]
Copyright 1996-2024 Glyph & Cog, LLC

테스트 삼아 IRS 웹사이트에서 Form W-4 PDF를 다운로드해 pdftotext에 넣어 보았습니다.

$ ./result/bin/pdftotext fw4.pdf /dev/stdout | head -n 5
Form W-4
Department of the Treasury Internal Revenue Service

Employee's Withholding Certificate
Complete Form W-4 so that your employer can withhold the correct federal income tax from your pay. Give Form W-4 to your employer.

좋습니다, 제대로 동작하네요.

이 단계의 전체 소스는 GitLab에서 확인할 수 있습니다.

어리둥절할 정도로 쉬웠습니다

Nix가 어떻게 xpdf 바이너리를 빌드했는지 헷갈리신다면, 저 역시 마찬가지였습니다.

저는 Nix에게 xpdf의 빌드 과정을 알려준 적도 없는데, 어떻게 알았을까요?

알고 보니 제가 호출한 Nix의 mkDerivation 함수는 표준 make 빌드 과정을 가정하고 있었습니다.

for Unix packages that use the standard ./configure; make; make install build interface, you don’t need to write a build script at all; the standard environment does everything automatically. If stdenv doesn’t do what you need automatically, you can easily customise or override the various build phases.

“The Standard Environment” from the Nix Manual

그럼에도 제게는 조금 너무 마법처럼 느껴졌습니다.

xpdf 설명서에는 컴파일러에게 FreeType 헤더와 라이브러리 위치를 알려줘야 한다고 나와 있습니다. 저는 그런 작업을 전혀 하지 않았는데, Nix는 어떻게 프로젝트를 컴파일한 걸까요?

그리고 make install은 보통 /usr/bin 같은 시스템 전체 디렉터리에 쓰기를 하는데, 저는 sudo로 권한 상승을 한 적도 없는데 어떻게 된 걸까요?

저는 Nix가 암시적으로 make 빌드 순서를 호출하는 것에 더해, 환경 변수를 통해 조용히 빌드 과정을 제어하고 있을 거라 의심했습니다.

제 가설을 검증하기 위해 mkDerivation의 기본 installPhase 섹션을 모든 환경 변수를 출력하는 코드로 교체했습니다.

{
  xpdf = pkgs.stdenv.mkDerivation rec {
    ...
    installPhase = ''
      printenv
      make install
    '';

그런 다음 상세 로그를 켜고 nix build를 다시 실행했습니다.

nix build -L

아니나 다를까, CMAKE_INCLUDE_PATH 변수를 통해 FreeType 헤더를 가리키고 있었습니다.

CMAKE_INCLUDE_PATH=/nix/store/rmqyzrzpz2kzmn8329bc4fjmzvd33ylw-freetype-2.13.2-dev/include:...

그리고 제 /usr/bin 디렉터리를 덮어쓰지 않은 이유는 Nix가 CMake에게 Nix 전용 설치 디렉터리에 설치하도록 지시했기 때문이었습니다.

cmakeFlags=...-DCMAKE_INSTALL_BINDIR=/nix/store/7w4ql3kdrl3c0knnvx3lxsnrqfzfcy34-xpdf-4.05/bin

Nix의 이러한 동작은 양날의 검입니다. 잘 동작할 때는 제가 일일이 손을 잡고 이끌지 않아도 Nix가 빌드 과정을 알아서 처리해 주는 것이 마법처럼 느껴집니다. 하지만 동작하지 않았다면, Nix의 불투명한 추상화를 뚫고 문제를 디버깅해야 했을 것입니다.

honggfuzz로 xpdf 컴파일하기

이제 xpdf를 성공적으로 컴파일할 수 있게 되었으니, 워크플로에서 퍼즈 테스팅 부분을 도입할 차례입니다.

honggfuzz는 Google에서 관리하는 퍼즈 테스팅 도구입니다. 커버리지 가이드 퍼저(coverage-guided fuzzer)로, 특정 테스트 입력에 대해 대상 바이너리의 어떤 부분이 실행되는지 추적합니다. 바이너리가 새로운 코드 경로를 실행하도록 만드는 입력을 발견하면, 테스트되지 않은 동작을 건드릴 가능성이 더 높기 때문에 그 새로운 경로를 연 입력과 유사한 입력을 더 많이 생성합니다.

honggfuzz는 C와 C++ 컴파일러를 함께 제공하므로, honggfuzz로 xpdf를 컴파일하는 작업은 Nix가 기본 컴파일러 대신 honggfuzz의 컴파일러를 가리키도록 하는 것만큼 단순해야 합니다. 이를 위해 먼저 nativeBuildInputshonggfuzz 패키지를 추가해 컴파일 중에 사용할 수 있도록 합니다.

{
  xpdf = pkgs.stdenv.mkDerivation rec {
    ...
    nativeBuildInputs = with pkgs; [
      cmake
      honggfuzz
    ];
}

좋습니다, 이제 빌드 환경에서 honggfuzz를 사용할 수 있게 되었지만, CMake에게 기존에 사용하던 컴파일러 대신 honggfuzz 컴파일러를 사용하라고 어떻게 알려줄까요?

Make와 CMake는 각각 어떤 C 컴파일러와 C++ 컴파일러를 사용할지 지정하는 CCCXX 환경 변수를 따릅니다.

honggfuzz가 hfuzz-clang과 hfuzz-clang++라는 컴파일러를 함께 제공한다는 것을 알 수 있습니다. 꽤 유망해 보이지만, honggfuzz의 Nix 패키지에서 해당 바이너리를 어디서 찾을 수 있는지는 모르겠습니다. 다음과 같이 패키지를 탐색해 봅니다.

$ nix build nixpkgs#honggfuzz

$ find -L result -type f -name hfuzz-clang
result/bin/hfuzz-clang

좋습니다, 이를 통해 honggfuzz Nix 패키지의 컴파일러가 bin/ 하위 디렉터리에 있다는 것을 알 수 있습니다.

Nix가 honggfuzz 컴파일러를 사용해 xpdf를 빌드하도록 하기 위해 CCCXX 변수가 올바른 컴파일러 경로를 가리키도록 합니다.

{
    xpdf = pkgs.stdenv.mkDerivation rec {
      ...

      preConfigure = ''
        export CC=${pkgs.honggfuzz}/bin/hfuzz-clang
        export CXX=${pkgs.honggfuzz}/bin/hfuzz-clang++
      '';
}

상세 출력과 함께 빌드해 보면 실제로 Nix가 honggfuzz 컴파일러를 사용하고 있음을 알 수 있습니다.

$ nix build -L
...
xpdf> -- The C compiler identification is Clang 16.0.6
xpdf> -- The CXX compiler identification is Clang 16.0.6
...
xpdf> -- Check for working C compiler: /nix/store/kb9vkjv4admbdixrjyanfb1i9dd3cbmm-honggfuzz-2.6/bin/hfuzz-clang - skipped
...
xpdf> -- Check for working CXX compiler: /nix/store/kb9vkjv4admbdixrjyanfb1i9dd3cbmm-honggfuzz-2.6/bin/hfuzz-clang++ - skipped

이 시점에서 flake.nix다음과 같이 생겼어야 합니다.

개발 셸에서 즉석 퍼징하기

honggfuzz 컴파일러로 xpdf를 컴파일했지만, 이제 본격적으로 재미있는 부분을 해보고 싶습니다.

Nix flake 안에 퍼징을 시작하는 우아한 명령어를 만들 수도 있지만, 지금은 최대한 빨리 직접 손을 더럽히며 이것저것 만져보고 싶습니다. 그러기 위해 모든 도구를 사용할 수 있는 Nix 개발 셸을 만듭니다.

Nix 개발 셸을 만들기 위해 Nix flake에 다음 내용을 추가합니다.

{
    packages = rec {
        ...
    };

    devShells.default = pkgs.mkShell {
      buildInputs = self.packages.${system}.xpdf.nativeBuildInputs ++ (with pkgs; [
        wget
      ]);

      shellHook = ''
        wget --version | head -n 1
      '';
    };

이 시점에서 flake.nix다음과 같이 생겼어야 합니다.

nix develop을 입력해 Nix 개발 셸에 진입합니다.

$ nix develop
GNU Wget 1.21.4 built on linux-gnu.

“GNU Wget” 출력은 셸 안에서 사용할 수 있는 도구들의 버전 번호를 출력하는 shellHook에서 나온 것입니다. honggfuzz 바이너리도 사용할 수 있습니다.

$ honggfuzz --help 2>&1 | head -n 1
Usage: honggfuzz [options] -- path_to_command [args]

이렇게 동작하는 이유는 mkShell 안에서 buildInputs를 xpdf 패키지의 모든 nativeBuildInputs(cmakehonggfuzz)에 PDF 다운로드를 위해 개발 셸에서만 쓰고 싶은 wget을 더한 것으로 지정했기 때문입니다.

다음으로 퍼즈 결과를 저장할 디렉터리를 만듭니다. 아직은 실험 단계이므로 임시 디렉터리를 사용합니다.

PDF_DIR="$(mktemp --directory)"

그런 다음 샘플 입력으로 사용할 PDF를 하나 가져옵니다.

$ PDF_URL='https://www.irs.gov/pub/irs-pdf/fw4.pdf' && \
  wget --directory-prefix="${PDF_DIR}" "${PDF_URL}"

./result/bin 폴더 아래에 pdftotext가 실행 준비를 마쳤는지 확인하기 위해 nix build를 한 번 더 실행합니다.

$ nix build && ./result/bin/pdftotext -v
pdftotext version 4.05 [www.xpdfreader.com]
Copyright 1996-2024 Glyph & Cog, LLC

마침내 진실의 순간입니다. honggfuzz의 테스트 러너를 실행합니다.

$ honggfuzz \
    --input "${PDF_DIR}" \
    -- ./result/bin/pdftotext ___FILE___

동작 방식은 다음과 같습니다.

  • --input "${PDF_DIR}"는 변형할 입력 파일들이 있는 디렉터리를 지정합니다.
  • -- ./result/bin/pdftotext ___FILE___: 퍼즈할 대상 프로그램을 지정합니다. ___FILE___은 플레이스홀더 매개변수입니다. honggfuzz는 매 실행마다 새로 생성된 파일의 경로로 이를 교체합니다.

명령어를 실행하자 honggfuzz 퍼징 인터페이스가 반겨줍니다.

honggfuzz가 퍼즈 테스팅 진행 상황을 보여주는 터미널 UI를 표시합니다

성공했습니다! honggfuzz를 며칠 동안 돌려서 무언가 잡히는지 지켜볼 수도 있지만, 버그를 찾을 확률을 높이기 위해 워크플로를 조금 더 다듬고 싶습니다.

다음 편: Nix로 xpdf의 패치되지 않은 버그 찾기

지금까지 Nix와 honggfuzz를 사용해 xpdf PDF 리더에 대해 기본적인 퍼즈 테스팅을 수행하는 방법을 보여드렸습니다.

후속 글에서는 다음 내용을 다루겠습니다.

  • 전체 퍼징 워크플로를 자동화하는 방법
  • 크래시를 유발할 가능성이 더 높은 까다로운 PDF를 수집하는 방법
  • xpdf 최신 버전에서 패치되지 않은 버그를 찾는 방법

아래 글에서 이어서 읽어 보세요.


이 작업의 기반이 된 Fuzzing101 튜토리얼 시리즈를 만든 Antonio Morales에게 감사드립니다.

원문은 Michael Lynch님이 에 게재했습니다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.