Passing `-fsanitize=fuzzer` via the command line or from a `CMakePresets` file

When using clang as a compiler and trying to set CMAKE_C_FLAGS to include -fsanitize=fuzzer, generation fails with:

$ cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_C_FLAGS="-fsanitize=fuzzer"
-- The C compiler identification is Clang 14.0.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - failed
-- Check for working C compiler: /usr/bin/clang
-- Check for working C compiler: /usr/bin/clang - broken
CMake Error at /home/cristi/.local/lib/python3.9/site-packages/cmake/data/share/cmake-3.22/Modules/CMakeTestCCompiler.cmake:69 (message):
  The C compiler

    "/usr/bin/clang"

  is not able to compile a simple test program.

  It fails with the following output:

    Change Dir: /home/cristi/sources/sample/build/CMakeFiles/CMakeTmp

    Run Build Command(s):/usr/bin/gmake -f Makefile cmTC_da524/fast && /usr/bin/gmake  -f CMakeFiles/cmTC_da524.dir/build.make CMakeFiles/cmTC_da524.dir/build
    gmake[1]: Entering directory '/home/cristi/sources/sample/build/CMakeFiles/CMakeTmp'
    Building C object CMakeFiles/cmTC_da524.dir/testCCompiler.c.o
    /usr/bin/clang   -fsanitize=fuzzer  -MD -MT CMakeFiles/cmTC_da524.dir/testCCompiler.c.o -MF CMakeFiles/cmTC_da524.dir/testCCompiler.c.o.d -o CMakeFiles/cmTC_da524.dir/testCCompiler.c.o -c /home/cristi/sources/sample/build/CMakeFiles/CMakeTmp/testCCompiler.c
    Linking C executable cmTC_da524
    /home/cristi/.local/lib/python3.9/site-packages/cmake/data/bin/cmake -E cmake_link_script CMakeFiles/cmTC_da524.dir/link.txt --verbose=1
    /usr/bin/clang -fsanitize=fuzzer  CMakeFiles/cmTC_da524.dir/testCCompiler.c.o -o cmTC_da524
    /usr/bin/ld: CMakeFiles/cmTC_da524.dir/testCCompiler.c.o: in function `main':
    testCCompiler.c:(.text.main[main]+0x0): multiple definition of `main'; /usr/lib/llvm-14/lib/clang/14.0.0/lib/linux/libclang_rt.fuzzer-x86_64.a(FuzzerMain.cpp.o):(.text.main+0x0): first defined here
    /usr/bin/ld: /usr/lib/llvm-14/lib/clang/14.0.0/lib/linux/libclang_rt.fuzzer-x86_64.a(FuzzerMain.cpp.o): in function `main':
    (.text.main+0x12): undefined reference to `LLVMFuzzerTestOneInput'
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    gmake[1]: *** [CMakeFiles/cmTC_da524.dir/build.make:100: cmTC_da524] Error 1
    gmake[1]: Leaving directory '/home/cristi/sources/sample/build/CMakeFiles/CMakeTmp'
    gmake: *** [Makefile:127: cmTC_da524/fast] Error 2





  CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
  CMakeLists.txt:3 (project)


-- Configuring incomplete, errors occurred!
See also "/home/cristi/sources/sample/build/CMakeFiles/CMakeOutput.log".
See also "/home/cristi/sources/sample/build/CMakeFiles/CMakeError.log".

The same thing happens if a CMakePresets.json file is used. For reference, here is a minimal example:

main.c:

#include <stdint.h>
#include <stdlib.h>

int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
    return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.22)

project(sample C)
add_executable(sample main.c)

CMakePresets.json:

{
    "version": 3,
    "cmakeMinimumRequired": {
        "major": 3,
        "minor": 22,
        "patch": 0
    },
    "configurePresets": [
        {
            "name": "demo",
            "binaryDir": "${sourceDir}/build/${presetName}",
            "cacheVariables": {
                "CMAKE_C_COMPILER": "clang",
                "CMAKE_C_FLAGS": "-fsanitize=fuzzer"
            }
        }
    ],
    "buildPresets": [
        {
            "name": "demo",
            "configurePreset": "demo"
        }
    ]
}

This is easy to workaround by setting the flags from the CMakeLists.txt itself, but I’m just curious if doing things like this is supposed to be working.

I’m using cmake 3.22.0 on Ubuntu 22.04.2 LTS with clang 14.0.0.

A quick bit of searching online seems to indicate that the -fsanitize=fuzzer option will link your executable with a library that provides its own main(). I suggest you chase down the official clang docs and see what it says about how to use that option in the correct way.

The full context for this is that I have a fuzzing target and I don’t want to have my own main in that case, I just provide an implementation for LLVMFuzzerTestOneInput. I was initially doing compiler checks in CMakeLists.txt and adding build/link options as needed (I want different flags when I build my fuzzing targets with say hfuzz-clang vs afl-clang-lto), but I wanted to move away from that and have some presets that will make my life easier. I’m always building for fuzzing in this case, so I do not care about non-fuzzing use cases.

According to the clang docs, passing -fsanitize=fuzzer is enough (using -fsanitize=fuzzer-no-link is not an option in my case).

Compiling directly with clang does work, in case that wasn’t clear:

$ clang main.c -fsanitize=fuzzer -o demo
$ ./demo
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1789342877
INFO: Loaded 1 modules   (1 inline 8-bit counters): 1 [0x5643b987efa0, 0x5643b987efa1),
INFO: Loaded 1 PC tables (1 PCs): 1 [0x5643b987efa8,0x5643b987efb8),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 1 ft: 1 corp: 1/1b exec/s: 0 rss: 26Mb
#2097152        pulse  cov: 1 ft: 1 corp: 1/1b lim: 4096 exec/s: 1048576 rss: 26Mb
#4194304        pulse  cov: 1 ft: 1 corp: 1/1b lim: 4096 exec/s: 838860 rss: 26Mb
^C==101== libFuzzer: run interrupted; exiting

Of course, CMake can’t know this and when it checks for a working C compiler it tries to compile something with a main. Is there a way to make this work?

I’m currently doing something similar to:

if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
    target_compile_options(my_target PRIVATE -fsanitize=fuzzer)
    target_link_libraries(my_target PRIVATE -fsanitize=fuzzer)
endif ()

I just wanted to move away from that.

This seems to me like a case where the project is the better place to put this logic, not the toolchain file. CMake wants to test the toolchain, but if you force it to use -fsanitize=fuzzer for every compilation, that kinda breaks the model CMake has for what the flags and initial compiler setup does. That said, if you really want to stay on your current path, you can set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY in your toolchain file. This will tell CMake to build a static library rather than an executable when doing its compiler checks. This effectively means the linker isn’t checked, and for the sort of platform you’re targeting, this feels to me like a bit of an abuse of what this variable was meant for, but it’s there if you decide you’re ok with it.

1 Like

I’m ok with it. The real use case is a bit more complex than these examples and this greatly simplifies CMakeLists.txt. Since it is already specialized just for building some fuzzers I don’t worry about braking something with this.

Thanks!