1# afl-clang-lto - collision free instrumentation at link time 2 3## TL;DR: 4 5This version requires a LLVM 12 or newer. 6 71. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run 8 slightly faster and give better coverage. 9 102. You can use it together with COMPCOV, COMPLOG and the instrument file 11 listing features. 12 133. It only works with LLVM 12 or newer. 14 154. AUTODICTIONARY feature (see below) 16 175. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib AS=llvm-as`. 18 Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. 19 20## Introduction and problem description 21 22A big issue with how vanilla AFL worked was that the basic block IDs that are 23set during compilation are random - and hence naturally the larger the number 24of instrumented locations, the higher the number of edge collisions are in the 25map. This can result in not discovering new paths and therefore degrade the 26efficiency of the fuzzing process. 27 28*This issue is underestimated in the fuzzing community* With a 2^16 = 64kb 29standard map at already 256 instrumented blocks, there is on average one 30collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence 31the real collisions are between 750-18.000! 32 33Note that PCGUARD (our own modified implementation and the SANCOV PCGUARD 34implementation from libfuzzer) also provides collision free coverage. 35It is a bit slower though and can a few targets with very early constructors. 36 37* We instrument at link time when we have all files pre-compiled. 38* To instrument at link time, we compile in LTO (link time optimization) mode. 39* Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the correct 40 LTO options and runs our own afl-ld linker instead of the system linker. 41* The LLVM linker collects all LTO files to link and instruments them so that we 42 have non-colliding edge coverage. 43* We use a new (for afl) edge coverage - which is the same as in llvm 44 -fsanitize=coverage edge coverage mode. :) 45 46The result: 47 48* 10-25% speed gain compared to llvm_mode 49* guaranteed non-colliding edge coverage 50* The compile time, especially for binaries to an instrumented library, can be 51 much (and sometimes much much) longer. 52 53Example build output from a libtiff build: 54 55``` 56libtool: link: afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o ../libtiff/.libs/libtiff.a ../port/.libs/libport.a -llzma -ljbig -ljpeg -lz -lm 57afl-clang-lto++2.63d by Marc "vanHauser" Heuse <[email protected]> in mode LTO 58afl-llvm-lto++2.63d by Marc "vanHauser" Heuse <[email protected]> 59AUTODICTIONARY: 11 strings found 60[+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode). 61``` 62 63## Getting LLVM 12+ 64 65### Installing llvm 66 67The best way to install LLVM is to follow [https://apt.llvm.org/](https://apt.llvm.org/) 68 69e.g. for LLVM 15: 70``` 71wget https://apt.llvm.org/llvm.sh 72chmod +x llvm.sh 73sudo ./llvm.sh 15 all 74``` 75 76LLVM 12 to 18 should be available in all current Linux repositories. 77 78## How to build afl-clang-lto 79 80That part is easy. 81Just set `LLVM_CONFIG` to the llvm-config-VERSION and build AFL++, e.g. for 82LLVM 15: 83 84``` 85cd ~/AFLplusplus 86export LLVM_CONFIG=llvm-config-15 87make 88sudo make install 89``` 90 91## How to use afl-clang-lto 92 93Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. 94 95Also, the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> 96[README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov 97(AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. 98 99Example (note that you might need to add the version, e.g. `llvm-ar-15`: 100 101``` 102CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar AS=llvm-as ./configure 103make 104``` 105 106NOTE: some targets also need to set the linker, try both `afl-clang-lto` and 107`afl-ld-lto` for `LD=` before `configure`. 108 109## Instrumenting shared libraries 110 111Note: this is highly discouraged! Try to compile to static libraries with 112afl-clang-lto instead of shared libraries! 113 114To make instrumented shared libraries work with afl-clang-lto, you have to do 115quite some extra steps. 116 117Every shared library you want to instrument has to be individually compiled. The 118environment variable `AFL_LLVM_LTO_DONTWRITEID=1` has to be set during 119compilation. Additionally, the environment variable `AFL_LLVM_LTO_STARTID` has 120to be set to the added edge count values of all previous compiled instrumented 121shared libraries for that target. E.g., for the first shared library this would 122be `AFL_LLVM_LTO_STARTID=0` and afl-clang-lto will then report how many edges 123have been instrumented (let's say it reported 1000 instrumented edges). The 124second shared library then has to be set to that value 125(`AFL_LLVM_LTO_STARTID=1000` in our example), for the third to all previous 126counts added, etc. 127 128The final program compilation step then may *not* have 129`AFL_LLVM_LTO_DONTWRITEID` set, and `AFL_LLVM_LTO_STARTID` must be set to all 130edge counts added of all shared libraries it will be linked to. 131 132This is quite some hands-on work, so better stay away from instrumenting shared 133libraries. :-) 134 135## AUTODICTIONARY feature 136 137While compiling, a dictionary based on string comparisons is automatically 138generated and put into the target binary. This dictionary is transferred to 139afl-fuzz on start. This improves coverage statistically by 5-10%. :) 140 141Note that if for any reason you do not want to use the autodictionary feature, 142then just set the environment variable `AFL_NO_AUTODICT` when starting afl-fuzz. 143 144## Fixed memory map 145 146To speed up fuzzing a little bit more, it is possible to set a fixed shared 147memory map. Recommended is the value 0x10000. 148 149In most cases, this will work without any problems. However, if a target uses 150early constructors, ifuncs, or a deferred forkserver, this can crash the target. 151 152Also, on unusual operating systems/processors/kernels or weird libraries the 153recommended 0x10000 address might not work, so then change the fixed address. 154 155To enable this feature, set `AFL_LLVM_MAP_ADDR` with the address. 156 157## Document edge IDs 158 159Setting `export AFL_LLVM_DOCUMENT_IDS=file` will document in a file which edge 160ID was given to which function. This helps to identify functions with variable 161bytes or which functions were touched by an input. 162 163## Solving difficult targets 164 165Some targets are difficult because the configure script does unusual stuff that 166is unexpected for afl. See the next section `Potential issues` for how to solve 167these. 168 169### Example: ffmpeg 170 171An example of a hard to solve target is ffmpeg. Here is how to successfully 172instrument it: 173 1741. Get and extract the current ffmpeg and change to its directory. 175 1762. Running configure with --cc=clang fails and various other items will fail 177 when compiling, so we have to trick configure: 178 179 ``` 180 ./configure --enable-lto --disable-shared --disable-inline-asm 181 ``` 182 1833. Now the configuration is done - and we edit the settings in 184 `./ffbuild/config.mak` (-: the original line, +: what to change it into): 185 186 ``` 187 -CC=gcc 188 +CC=afl-clang-lto 189 -CXX=g++ 190 +CXX=afl-clang-lto++ 191 -AS=gcc 192 +AS=llvm-as 193 -LD=gcc 194 +LD=afl-clang-lto++ 195 -DEPCC=gcc 196 +DEPCC=afl-clang-lto 197 -DEPAS=gcc 198 +DEPAS=afl-clang-lto++ 199 -AR=ar 200 +AR=llvm-ar 201 -AR_CMD=ar 202 +AR_CMD=llvm-ar 203 -NM_CMD=nm -g 204 +NM_CMD=llvm-nm -g 205 -RANLIB=ranlib -D 206 +RANLIB=llvm-ranlib -D 207 ``` 208 2094. Then type make, wait for a long time, and you are done. :) 210 211### Example: WebKit jsc 212 213Building jsc is difficult as the build script has bugs. 214 2151. Checkout Webkit: 216 217 ``` 218 svn checkout https://svn.webkit.org/repository/webkit/trunk WebKit 219 cd WebKit 220 ``` 221 2222. Fix the build environment: 223 224 ``` 225 mkdir -p WebKitBuild/Release 226 cd WebKitBuild/Release 227 ln -s ../../../../../usr/bin/llvm-ar-12 llvm-ar-12 228 ln -s ../../../../../usr/bin/llvm-ranlib-12 llvm-ranlib-12 229 cd ../.. 230 ``` 231 2323. Build. :) 233 234 ``` 235 Tools/Scripts/build-jsc --jsc-only --cli --cmakeargs="-DCMAKE_AR='llvm-ar-12' -DCMAKE_RANLIB='llvm-ranlib-12' -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CC_FLAGS='-O3 -lrt' -DCMAKE_CXX_FLAGS='-O3 -lrt' -DIMPORTED_LOCATION='/lib/x86_64-linux-gnu/' -DCMAKE_CC=afl-clang-lto -DCMAKE_CXX=afl-clang-lto++ -DENABLE_STATIC_JSC=ON" 236 ``` 237 238## Potential issues 239 240### Compiling libraries fails 241 242If you see this message: 243 244``` 245/bin/ld: libfoo.a: error adding symbols: archive has no index; run ranlib to add one 246``` 247 248This is because usually gnu gcc ranlib is being called which cannot deal with 249clang LTO files. The solution is simple: when you `./configure`, you also have 250to set `RANLIB=llvm-ranlib` and `AR=llvm-ar`. 251 252Solution: 253 254``` 255AR=llvm-ar RANLIB=llvm-ranlib CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --disable-shared 256``` 257 258And on some targets you have to set `AR=/RANLIB=` even for `make` as the 259configure script does not save it. Other targets ignore environment variables 260and need the parameters set via `./configure --cc=... --cxx= --ranlib= ...` etc. 261(I am looking at you ffmpeg!) 262 263If you see this message: 264 265``` 266assembler command failed ... 267``` 268 269Then try setting `llvm-as` for configure: 270 271``` 272AS=llvm-as ... 273``` 274 275### Compiling programs still fail 276 277afl-clang-lto is still work in progress. 278 279Known issues: 280* Anything that LLVM 12+ cannot compile, afl-clang-lto cannot compile either - 281 obviously. 282* Anything that does not compile with LTO, afl-clang-lto cannot compile either - 283 obviously. 284 285Hence, if building a target with afl-clang-lto fails, try to build it with 286LLVM 12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`, 287and `CXXFLAGS=-flto=full`). 288 289If this succeeds, then there is an issue with afl-clang-lto. Please report at 290[https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226). 291 292Even some targets where clang-12 fails can be built if the fail is just in 293`./configure`, see `Solving difficult targets` above. 294 295## History 296 297This was originally envisioned by hexcoder- in Summer 2019. However, we saw no 298way to create a pass that is run at link time - although there is a option for 299this in the PassManager: EP_FullLinkTimeOptimizationLast. ("Fun" info - nobody 300knows what this is doing. And the developer who implemented this didn't respond 301to emails.) 302 303In December then came the idea to implement this as a pass that is run via the 304LLVM "opt" program, which is performed via an own linker that afterwards calls 305the real linker. This was first implemented in January and work ... kinda. The 306LTO time instrumentation worked, however, "how" the basic blocks were 307instrumented was a problem, as reducing duplicates turned out to be very, very 308difficult with a program that has so many paths and therefore so many 309dependencies. A lot of strategies were implemented - and failed. And then sat 310solvers were tried, but with over 10.000 variables that turned out to be a 311dead-end too. 312 313The final idea to solve this came from domenukk who proposed to insert a block 314into an edge and then just use incremental counters ... and this worked! After 315some trials and errors to implement this vanhauser-thc found out that there is 316actually an LLVM function for this: SplitEdge() :-) 317 318Still more problems came up though as this only works without bugs from LLVM 9 319onwards, and with high optimization the link optimization ruins the instrumented 320control flow graph. 321 322This is all now fixed with LLVM 12+. The llvm's own linker is now able to load 323passes and this bypasses all problems we had. 324 325Happy end :) 326