1{ lib, stdenv, stdenvNoCC, fetchFromGitHub, callPackage, makeWrapper
2, clang, llvm, gcc, which, libcgroup, python, perl, gmp
3, file, wine ? null, fetchpatch
4}:
5
6# wine fuzzing is only known to work for win32 binaries, and using a mixture of
7# 32 and 64-bit libraries ... complicates things, so it's recommended to build
8# a full 32bit version of this package if you want to do wine fuzzing
9assert (wine != null) -> (stdenv.targetPlatform.system == "i686-linux");
10
11let
12 aflplusplus-qemu = callPackage ./qemu.nix { inherit aflplusplus; };
13 qemu-exe-name = if stdenv.targetPlatform.system == "x86_64-linux" then "qemu-x86_64"
14 else if stdenv.targetPlatform.system == "i686-linux" then "qemu-i386"
15 else throw "aflplusplus: no support for ${stdenv.targetPlatform.system}!";
16 libdislocator = callPackage ./libdislocator.nix { inherit aflplusplus; };
17 libtokencap = callPackage ./libtokencap.nix { inherit aflplusplus; };
18 aflplusplus = stdenvNoCC.mkDerivation rec {
19 pname = "aflplusplus";
20 version = "2.65c";
21
22 src = fetchFromGitHub {
23 owner = "AFLplusplus";
24 repo = "AFLplusplus";
25 rev = version;
26 sha256 = "1np2a3kypb2m8nyv6qnij18yzn41pl8619jzydci40br4vxial9l";
27 };
28 enableParallelBuilding = true;
29
30 # Note: libcgroup isn't needed for building, just for the afl-cgroup
31 # script.
32 nativeBuildInputs = [ makeWrapper which clang gcc ];
33 buildInputs = [ llvm python gmp ]
34 ++ lib.optional (wine != null) python.pkgs.wrapPython;
35
36
37 postPatch = ''
38 # Replace the CLANG_BIN variables with the correct path
39 substituteInPlace llvm_mode/afl-clang-fast.c \
40 --replace "CLANGPP_BIN" '"${clang}/bin/clang++"' \
41 --replace "CLANG_BIN" '"${clang}/bin/clang"' \
42 --replace 'getenv("AFL_PATH")' "(getenv(\"AFL_PATH\") ? getenv(\"AFL_PATH\") : \"$out/lib/afl\")"
43
44 # Replace "gcc" and friends with full paths in afl-gcc
45 # Prevents afl-gcc picking up any (possibly incorrect) gcc from the path
46 substituteInPlace src/afl-gcc.c \
47 --replace '"gcc"' '"${gcc}/bin/gcc"' \
48 --replace '"g++"' '"${gcc}/bin/g++"' \
49 --replace '"gcj"' '"gcj-UNSUPPORTED"' \
50 --replace '"clang"' '"clang-UNSUPPORTED"' \
51 --replace '"clang++"' '"clang++-UNSUPPORTED"'
52 '';
53
54 env.NIX_CFLAGS_COMPILE = toString [
55 # Needed with GCC 12
56 "-Wno-error=use-after-free"
57 ];
58
59 makeFlags = [ "PREFIX=$(out)" ];
60 buildPhase = ''
61 common="$makeFlags -j$NIX_BUILD_CORES"
62 make all $common
63 make radamsa $common
64 make -C gcc_plugin CC=${gcc}/bin/gcc CXX=${gcc}/bin/g++ $common
65 make -C llvm_mode $common
66 make -C qemu_mode/libcompcov $common
67 make -C qemu_mode/unsigaction $common
68 '';
69
70 postInstall = ''
71 # remove afl-clang(++) which are just symlinks to afl-clang-fast
72 rm $out/bin/afl-clang $out/bin/afl-clang++
73
74 # the makefile neglects to install unsigaction
75 cp qemu_mode/unsigaction/unsigaction*.so $out/lib/afl/
76
77 # Install the custom QEMU emulator for binary blob fuzzing.
78 cp ${aflplusplus-qemu}/bin/${qemu-exe-name} $out/bin/afl-qemu-trace
79
80 # give user a convenient way of accessing libcompconv.so, libdislocator.so, libtokencap.so
81 cat > $out/bin/get-afl-qemu-libcompcov-so <<END
82 #!${stdenv.shell}
83 echo $out/lib/afl/libcompcov.so
84 END
85 chmod +x $out/bin/get-afl-qemu-libcompcov-so
86 cp ${libdislocator}/bin/get-libdislocator-so $out/bin/
87 cp ${libtokencap}/bin/get-libtokencap-so $out/bin/
88
89 # Install the cgroups wrapper for asan-based fuzzing.
90 cp examples/asan_cgroups/limit_memory.sh $out/bin/afl-cgroup
91 chmod +x $out/bin/afl-cgroup
92 substituteInPlace $out/bin/afl-cgroup \
93 --replace "cgcreate" "${libcgroup}/bin/cgcreate" \
94 --replace "cgexec" "${libcgroup}/bin/cgexec" \
95 --replace "cgdelete" "${libcgroup}/bin/cgdelete"
96
97 patchShebangs $out/bin
98
99 '' + lib.optionalString (wine != null) ''
100 substitute afl-wine-trace $out/bin/afl-wine-trace \
101 --replace "qemu_mode/unsigaction" "$out/lib/afl"
102 chmod +x $out/bin/afl-wine-trace
103
104 # qemu needs to be fed ELFs, not wrapper scripts, so we have to cheat a bit if we
105 # detect a wrapped wine
106 for winePath in ${wine}/bin/.wine ${wine}/bin/wine; do
107 if [ -x $winePath ]; then break; fi
108 done
109 makeWrapperArgs="--set-default 'AFL_WINE_PATH' '$winePath'" \
110 wrapPythonProgramsIn $out/bin ${python.pkgs.pefile}
111 '';
112
113 nativeInstallCheckInputs = [ perl file ];
114 doInstallCheck = true;
115 installCheckPhase = ''
116 # replace references to tools in build directory with references to installed locations
117 substituteInPlace test/test.sh \
118 --replace '../libcompcov.so' '`$out/bin/get-afl-qemu-libcompcov-so`' \
119 --replace '../libdislocator.so' '`$out/bin/get-libdislocator-so`' \
120 --replace '../libtokencap.so' '`$out/bin/get-libtokencap-so`'
121 perl -pi -e 's|(?<!\.)(?<!-I)(\.\./)([^\s\/]+?)(?<!\.c)(?<!\.s?o)(?=\s)|\$out/bin/\2|g' test/test.sh
122 cd test && ./test.sh
123 '';
124
125 passthru = {
126 inherit libdislocator libtokencap;
127 qemu = aflplusplus-qemu;
128 };
129
130 meta = {
131 description = ''
132 A heavily enhanced version of AFL, incorporating many features
133 and improvements from the community
134 '';
135 homepage = "https://aflplus.plus";
136 license = lib.licenses.asl20;
137 platforms = ["x86_64-linux" "i686-linux"];
138 maintainers = with lib.maintainers; [ ris mindavi ];
139 };
140 };
141in aflplusplus