|
| 1 | +# Copyright (c) 2017, 2020, Oracle and/or its affiliates. |
| 2 | +# |
| 3 | +# This program is free software; you can redistribute it and/or modify |
| 4 | +# it under the terms of the GNU General Public License, version 2.0, |
| 5 | +# as published by the Free Software Foundation. |
| 6 | +# |
| 7 | +# This program is also distributed with certain software (including |
| 8 | +# but not limited to OpenSSL) that is licensed under separate terms, |
| 9 | +# as designated in a particular file or component or in included license |
| 10 | +# documentation. The authors of MySQL hereby grant you an additional |
| 11 | +# permission to link the program and your derivative works with the |
| 12 | +# separately licensed software that they have included with MySQL. |
| 13 | +# |
| 14 | +# This program is distributed in the hope that it will be useful, |
| 15 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | +# GNU General Public License for more details. |
| 18 | +# |
| 19 | +# You should have received a copy of the GNU General Public License |
| 20 | +# along with this program; if not, write to the Free Software |
| 21 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 22 | + |
| 23 | +INCLUDE(CMakePushCheckState) |
| 24 | + |
| 25 | +# Only supported for Clang/llvm |
| 26 | +IF(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") |
| 27 | + RETURN() |
| 28 | +ENDIF() |
| 29 | + |
| 30 | +# ld.lld: error: |
| 31 | +# /usr/lib64/clang/7.0.1/lib/linux/libclang_rt.fuzzer-x86_64.a |
| 32 | +# (FuzzerLoop.cpp.o): unsupported SHT_GROUP format |
| 33 | +IF(USE_LD_LLD AND C_LD_LLD_RESULT AND CXX_LD_LLD_RESULT) |
| 34 | + STRING(REPLACE "-fuse-ld=lld" "" |
| 35 | + CMAKE_C_LINK_FLAGS ${CMAKE_C_LINK_FLAGS}) |
| 36 | + STRING(REPLACE "-fuse-ld=lld" "" |
| 37 | + CMAKE_CXX_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS}) |
| 38 | + STRING(REPLACE "-Wl,--gdb-index" "" |
| 39 | + CMAKE_C_LINK_FLAGS ${CMAKE_C_LINK_FLAGS}) |
| 40 | + STRING(REPLACE "-Wl,--gdb-index" "" |
| 41 | + CMAKE_CXX_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS}) |
| 42 | +ENDIF() |
| 43 | + |
| 44 | +# check if clang knows about the coverage and trace-pc-guard |
| 45 | +# |
| 46 | +# compiler | CFLAGS | LDFLAGS |
| 47 | +# ----------|------------------------------------|------------------ |
| 48 | +# llvm 6.0+ | -fsanitize=fuzzer | -fsanitize=fuzzer |
| 49 | +# llvm 4.0 | -fsanitize-coverage=trace-pc-guard | -lFuzzer |
| 50 | +# llvm 3.9 | -fsanitize-coverage=trace-cmp | -lFuzzer |
| 51 | +# llvm 3.7 | -fsanitize-coverage=edge | -lFuzzer |
| 52 | + |
| 53 | +# llvm 4.0+ |
| 54 | +CMAKE_PUSH_CHECK_STATE(RESET) |
| 55 | +SET(CMAKE_REQUIRED_FLAGS "-fsanitize-coverage=trace-pc-guard") |
| 56 | +CHECK_CXX_COMPILER_FLAG("-fsanitize-coverage=trace-pc-guard" |
| 57 | + COMPILER_HAS_SANITIZE_COVERAGE_TRACE_PC_GUARD) |
| 58 | +CMAKE_POP_CHECK_STATE() |
| 59 | + |
| 60 | +# llvm 3.8+ |
| 61 | +CMAKE_PUSH_CHECK_STATE(RESET) |
| 62 | +SET(CMAKE_REQUIRED_FLAGS "-fsanitize-coverage=edge") |
| 63 | +CHECK_CXX_COMPILER_FLAG("-fsanitize-coverage=edge" |
| 64 | + COMPILER_HAS_SANITIZE_COVERAGE_TRACE_EDGE) |
| 65 | +CMAKE_POP_CHECK_STATE() |
| 66 | + |
| 67 | +# http://llvm.org/docs/LibFuzzer.html#tracing-cmp-instructions |
| 68 | +CMAKE_PUSH_CHECK_STATE(RESET) |
| 69 | +SET(CMAKE_REQUIRED_FLAGS "-fsanitize-coverage=trace-cmp") |
| 70 | +CHECK_CXX_COMPILER_FLAG("-fsanitize-coverage=trace-cmp" |
| 71 | + COMPILER_HAS_SANITIZE_COVERAGE_TRACE_CMP) |
| 72 | +CMAKE_POP_CHECK_STATE() |
| 73 | + |
| 74 | +CMAKE_PUSH_CHECK_STATE(RESET) |
| 75 | +SET(CMAKE_REQUIRED_FLAGS "-fprofile-instr-generate") |
| 76 | +CHECK_CXX_COMPILER_FLAG("-fprofile-instr-generate" |
| 77 | + COMPILER_HAS_PROFILE_INSTR_GENERATE) |
| 78 | +CMAKE_POP_CHECK_STATE() |
| 79 | + |
| 80 | +CMAKE_PUSH_CHECK_STATE(RESET) |
| 81 | +# invalid argument '-fcoverage-mapping' only allowed |
| 82 | +# with '-fprofile-instr-generate' |
| 83 | +SET(CMAKE_REQUIRED_FLAGS "-fprofile-instr-generate") |
| 84 | +CHECK_CXX_COMPILER_FLAG("-fcoverage-mapping" COMPILER_HAS_COVERAGE_MAPPING) |
| 85 | +CMAKE_POP_CHECK_STATE() |
| 86 | + |
| 87 | +CMAKE_PUSH_CHECK_STATE(RESET) |
| 88 | +SET(CMAKE_REQUIRED_LIBRARIES "-fsanitize=address,fuzzer") |
| 89 | +SET(CMAKE_REQUIRED_FLAGS "-fsanitize=address,fuzzer") |
| 90 | +CHECK_CXX_SOURCE_COMPILES(" |
| 91 | +extern \"C\" int LLVMFuzzerTestOneInput (void *, int) |
| 92 | +{ return 0; }" |
| 93 | +COMPILER_HAS_SANITIZE_FUZZER) |
| 94 | +CMAKE_POP_CHECK_STATE() |
| 95 | + |
| 96 | + |
| 97 | +IF(COMPILER_HAS_SANITIZE_FUZZER) |
| 98 | + SET(SANITIZE_COVERAGE_FLAGS "-fsanitize=address,fuzzer") |
| 99 | +ELSEIF(COMPILER_HAS_SANITIZE_COVERAGE_TRACE_PC_GUARD) |
| 100 | + SET(SANITIZE_COVERAGE_FLAGS "-fsanitize-coverage=trace-pc-guard") |
| 101 | +ELSEIF(COMPILER_HAS_SANITIZE_COVERAGE_TRACE_CMP) |
| 102 | + SET(SANITIZE_COVERAGE_FLAGS "-fsanitize-coverage=trace-cmp") |
| 103 | +ELSEIF(COMPILER_HAS_SANITIZE_COVERAGE_TRACE_EDGE) |
| 104 | + SET(SANITIZE_COVERAGE_FLAGS "-fsanitize-coverage=edge") |
| 105 | +ELSE() |
| 106 | + MESSAGE(WARNING "Coverage is not supported. Skiping build fuzz tests.") |
| 107 | +ENDIF() |
| 108 | + |
| 109 | +# check that libFuzzer is found |
| 110 | +# |
| 111 | +# check_library_exists() doesn't work here as it would provide a main() which |
| 112 | +# calls a test-function ... which collides with libFuzzer's main(): |
| 113 | +# |
| 114 | +# if the libFuzzer is found by the compiler it will provide a 'main()' and |
| 115 | +# require that we provide a 'LLVMFuzzerTestOneInput' at link-time. |
| 116 | +IF(SANITIZE_COVERAGE_FLAGS) |
| 117 | + CMAKE_PUSH_CHECK_STATE(RESET) |
| 118 | + SET(CMAKE_REQUIRED_LIBRARIES Fuzzer) |
| 119 | + SET(CMAKE_REQUIRED_FLAGS ${SANITIZE_COVERAGE_FLAGS}) |
| 120 | + CHECK_CXX_SOURCE_COMPILES(" |
| 121 | + extern \"C\" int LLVMFuzzerTestOneInput (void *, int) |
| 122 | + { return 0; }" |
| 123 | + CLANG_HAS_LIBFUZZER) |
| 124 | + CMAKE_POP_CHECK_STATE() |
| 125 | +ENDIF() |
| 126 | + |
| 127 | +IF(COMPILER_HAS_SANITIZE_FUZZER OR CLANG_HAS_LIBFUZZER) |
| 128 | + IF(CLANG_HAS_LIBFUZZER) |
| 129 | + SET(LIBFUZZER_LIBRARIES Fuzzer) |
| 130 | + ENDIF() |
| 131 | + SET(LIBFUZZER_LINK_FLAGS ${SANITIZE_COVERAGE_FLAGS}) |
| 132 | + SET(LIBFUZZER_COMPILE_FLAGS) |
| 133 | + LIST(APPEND LIBFUZZER_COMPILE_FLAGS ${SANITIZE_COVERAGE_FLAGS}) |
| 134 | + |
| 135 | + IF(COMPILER_HAS_PROFILE_INSTR_GENERATE) |
| 136 | + LIST(APPEND LIBFUZZER_COMPILE_FLAGS -fprofile-instr-generate) |
| 137 | + SET(LIBFUZZER_LINK_FLAGS |
| 138 | + "${LIBFUZZER_LINK_FLAGS} -fprofile-instr-generate") |
| 139 | + IF(COMPILER_HAS_COVERAGE_MAPPING) |
| 140 | + LIST(APPEND LIBFUZZER_COMPILE_FLAGS -fcoverage-mapping) |
| 141 | + ENDIF() |
| 142 | + ENDIF() |
| 143 | +ENDIF() |
| 144 | + |
| 145 | + |
| 146 | +FUNCTION(LIBFUZZER_ADD_TEST TARGET) |
| 147 | + SET(OPTS) |
| 148 | + SET(ONE_VAL_ARGS INITIAL_CORPUS_DIR MAX_TOTAL_TIME TIMEOUT) |
| 149 | + SET(MULTI_VAL_ARGS) |
| 150 | + CMAKE_PARSE_ARGUMENTS(ARG "${OPTS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) |
| 151 | + |
| 152 | + |
| 153 | + IF(NOT DEFINED ARG_MAX_TOTAL_TIME) |
| 154 | + SET(ARG_MAX_TOTAL_TIME 10) |
| 155 | + ENDIF() |
| 156 | + IF(NOT DEFINED ARG_TIMEOUT) |
| 157 | + SET(ARG_TIMEOUT 0.2) |
| 158 | + ENDIF() |
| 159 | + |
| 160 | + SET(BINARY_CORPUS_DIR "${CMAKE_CURRENT_BINARY_DIR}/corpus/${TARGET}") |
| 161 | + SET(BINARY_ARTIFACT_DIR "${CMAKE_CURRENT_BINARY_DIR}/artifacts/${TARGET}") |
| 162 | + |
| 163 | + SET_TARGET_PROPERTIES( |
| 164 | + ${TARGET} |
| 165 | + PROPERTIES |
| 166 | + COMPILE_OPTIONS "${LIBFUZZER_COMPILE_FLAGS}" |
| 167 | + LINK_FLAGS "${LIBFUZZER_LINK_FLAGS}" |
| 168 | + ) |
| 169 | + |
| 170 | + IF(LIBFUZZER_LIBRARIES) |
| 171 | + TARGET_LINK_LIBRARIES(${TARGET} ${LIBFUZZER_LIBRARIES}) |
| 172 | + ENDIF() |
| 173 | + |
| 174 | + # reset the artifact and corpus dir if the binary changed. |
| 175 | + ADD_CUSTOM_COMMAND(TARGET ${TARGET} |
| 176 | + POST_BUILD |
| 177 | + COMMAND ${CMAKE_COMMAND} -E remove_directory "${BINARY_CORPUS_DIR}" |
| 178 | + COMMAND ${CMAKE_COMMAND} -E make_directory "${BINARY_CORPUS_DIR}" |
| 179 | + COMMAND ${CMAKE_COMMAND} -E remove_directory "${BINARY_ARTIFACT_DIR}" |
| 180 | + COMMAND ${CMAKE_COMMAND} -E make_directory "${BINARY_ARTIFACT_DIR}" |
| 181 | + ) |
| 182 | + |
| 183 | + IF(ARG_INITIAL_CORPUS_DIR) |
| 184 | + # prepare the corpus in the build-dir based on samples from the source-dir |
| 185 | + ADD_CUSTOM_COMMAND(TARGET ${TARGET} |
| 186 | + POST_BUILD |
| 187 | + COMMAND $<TARGET_FILE:${TARGET}> |
| 188 | + -merge=1 |
| 189 | + -verbosity=0 |
| 190 | + -merge_control_file="${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.control" |
| 191 | + "${ARG_INITIAL_CORPUS_DIR}" "${BINARY_CORPUS_DIR}" 2> /dev/null |
| 192 | + COMMENT "Preparing corpus for ${TARGET}" |
| 193 | + ) |
| 194 | + ENDIF() |
| 195 | + |
| 196 | + # use cmake -E env to set the LLVM_PROFILE_FILE in a portable way |
| 197 | + ADD_TEST(${TARGET} |
| 198 | + ${CMAKE_BINARY_DIR}/bin/${TARGET} |
| 199 | + -max_total_time=${ARG_MAX_TOTAL_TIME} -timeout=${ARG_TIMEOUT} |
| 200 | + -artifact_prefix=${BINARY_ARTIFACT_DIR}/ |
| 201 | + ${BINARY_CORPUS_DIR} |
| 202 | + ) |
| 203 | +ENDFUNCTION() |
0 commit comments