Fuzzing Swift

The Swift compiler has built-in libFuzzer integration, making it possible to coverage-guided fuzz-test Swift code with the same LLVM libFuzzer engine used for C/C++. The official documentation lives in the Swift repository (docs/libFuzzerIntegration.md).

Defining a fuzz target

Annotate an entry-point function with @_cdecl("LLVMFuzzerTestOneInput"). It receives a raw pointer plus a byte count; converting that byte stream into whatever inputs the code under test needs is the fuzz target’s job:

@_cdecl("LLVMFuzzerTestOneInput")
public func test(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
  let bytes = UnsafeRawBufferPointer(start: start, count: count)
  // TODO: Test the code using the provided bytes.
  return 0
}

libFuzzer supplies the process main, the mutation engine, corpus management, and crash detection — the target only consumes bytes.

Compiling and running

Two flags are required: -sanitize=fuzzer links libFuzzer and enables coverage instrumentation, and -parse-as-library omits the main symbol so the fuzzer’s entry point is used instead:

swiftc -sanitize=fuzzer -parse-as-library myfile.swift
./myfile                       # start fuzzing

libFuzzer can be combined with other sanitizers, e.g. AddressSanitizer:

swiftc -sanitize=fuzzer,address -parse-as-library myfile.swift

All standard libFuzzer command-line options work on the resulting binary — -max_total_time, -jobs/-workers for parallelism, corpus directories, -artifact_prefix for crash output, and so on.

Practical notes

  • Toolchains matter. The libFuzzer integration depends on the compiler build; the Xcode-bundled Swift toolchain has historically omitted sanitizer/fuzzer components, while the downloadable toolchains from swift.org include them. Select a toolchain in Xcode or invoke one explicitly at the command line via xcrun --toolchain <id> swiftc.
  • Memory hygiene. libFuzzer never frees allocations between runs; long-running fuzz jobs of Objective-C-bridge-heavy Swift code should wrap iterations in autoreleasepool to avoid unbounded growth.
  • Sanitizer interactions. Not every sanitizer composes with every API — e.g. AddressSanitizer instrumentation is known to interfere with some system services on Apple platforms, so validate the harness with a short run before scaling out.

Sources