diff --git a/Tests/Fuzzing/CMakeLists.txt b/Tests/Fuzzing/CMakeLists.txt index 698524cb83..a78bf253ac 100644 --- a/Tests/Fuzzing/CMakeLists.txt +++ b/Tests/Fuzzing/CMakeLists.txt @@ -100,3 +100,6 @@ add_fuzzer(cmFileLockFuzzer cmFileLockFuzzer.cxx) # CMake script fuzzer add_fuzzer(cmScriptFuzzer cmScriptFuzzer.cxx) + +# Fortran dependency scanner fuzzer +add_fuzzer(cmFortranParserFuzzer cmFortranParserFuzzer.cxx) diff --git a/Tests/Fuzzing/cmFortranParser.dict b/Tests/Fuzzing/cmFortranParser.dict new file mode 100644 index 0000000000..440d30e16c --- /dev/null +++ b/Tests/Fuzzing/cmFortranParser.dict @@ -0,0 +1,36 @@ +# Fortran dependency scanner dictionary + +# Module dependency statements +"module " +"submodule " +"end module" +"use " +"use, intrinsic ::" +"only:" +"include " +"#include " + +# Submodule parent syntax +"submodule (" +")" +":" + +# Preprocessor directives the scanner tracks +"#define" +"#undef" +"#ifdef" +"#ifndef" +"#if defined" +"#elif" +"#else" +"#endif" + +# Fixed-form and continuation punctuation +"&" +"!" +"c " +"*" +"'" +"\"" +";" +"::" diff --git a/Tests/Fuzzing/cmFortranParserFuzzer.cxx b/Tests/Fuzzing/cmFortranParserFuzzer.cxx new file mode 100644 index 0000000000..9e325f1652 --- /dev/null +++ b/Tests/Fuzzing/cmFortranParserFuzzer.cxx @@ -0,0 +1,79 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +/* + * Fuzzer for CMake's Fortran dependency scanner + * + * CMake runs a flex lexer and a bison parser over Fortran sources to discover + * module dependencies: MODULE, SUBMODULE, USE and INCLUDE statements, plus the + * preprocessor directives that guard them. This fuzzer drives that scanner. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "cmFortranParser.h" +#include "cmSystemTools.h" + +static constexpr size_t kMaxInputSize = 256 * 1024; +static std::string g_testDir; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) +{ + (void)argc; + (void)argv; + + char tmpl[] = "/tmp/cmake_fuzz_fortran_XXXXXX"; + char* dir = mkdtemp(tmpl); + if (dir) { + g_testDir = dir; + } else { + g_testDir = "/tmp/cmake_fuzz_fortran"; + cmSystemTools::MakeDirectory(g_testDir); + } + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) +{ + if (size == 0 || size > kMaxInputSize) { + return 0; + } + + std::string const source = g_testDir + "/test.f90"; + { + FILE* fp = fopen(source.c_str(), "wb"); + if (!fp) { + return 0; + } + fwrite(data, 1, size, fp); + fclose(fp); + } + + cmFortranCompiler compiler; + compiler.Id = "GNU"; + compiler.SModSep = "@"; + compiler.SModExt = ".smod"; + + cmFortranSourceInfo info; + info.Source = source; + + std::vector const includes; + std::set const defines; + + cmFortranParser parser(compiler, includes, defines, info); + if (cmFortranParser_FilePush(&parser, source.c_str())) { + cmFortran_yyparse(parser.Scanner); + } + + unlink(source.c_str()); + + return 0; +} diff --git a/Tests/Fuzzing/corpus/fortran/continuation.f90 b/Tests/Fuzzing/corpus/fortran/continuation.f90 new file mode 100644 index 0000000000..ba14b3b4e8 --- /dev/null +++ b/Tests/Fuzzing/corpus/fortran/continuation.f90 @@ -0,0 +1,4 @@ +module a + use b + & + use c diff --git a/Tests/Fuzzing/corpus/fortran/fixedform.f b/Tests/Fuzzing/corpus/fortran/fixedform.f new file mode 100644 index 0000000000..eb464daab6 --- /dev/null +++ b/Tests/Fuzzing/corpus/fortran/fixedform.f @@ -0,0 +1,3 @@ + MODULE FIXED + USE OTHER + END diff --git a/Tests/Fuzzing/corpus/fortran/module.f90 b/Tests/Fuzzing/corpus/fortran/module.f90 new file mode 100644 index 0000000000..c31f1b7c6d --- /dev/null +++ b/Tests/Fuzzing/corpus/fortran/module.f90 @@ -0,0 +1,4 @@ +module geometry + implicit none + integer, parameter :: dp = kind(1.0d0) +end module geometry diff --git a/Tests/Fuzzing/corpus/fortran/preproc.F90 b/Tests/Fuzzing/corpus/fortran/preproc.F90 new file mode 100644 index 0000000000..f93a54077f --- /dev/null +++ b/Tests/Fuzzing/corpus/fortran/preproc.F90 @@ -0,0 +1,7 @@ +#define HAVE_MPI 1 +#ifdef HAVE_MPI + use mpi +#else + use serial_stub +#endif + include 'missing.inc' diff --git a/Tests/Fuzzing/corpus/fortran/submodule.f90 b/Tests/Fuzzing/corpus/fortran/submodule.f90 new file mode 100644 index 0000000000..935966117c --- /dev/null +++ b/Tests/Fuzzing/corpus/fortran/submodule.f90 @@ -0,0 +1,6 @@ +submodule (parent:child) impl + implicit none +contains + subroutine s() + end subroutine s +end submodule impl diff --git a/Tests/Fuzzing/corpus/fortran/use.f90 b/Tests/Fuzzing/corpus/fortran/use.f90 new file mode 100644 index 0000000000..e007ba0bb0 --- /dev/null +++ b/Tests/Fuzzing/corpus/fortran/use.f90 @@ -0,0 +1,5 @@ +program main + use geometry, only: dp + use, intrinsic :: iso_fortran_env + implicit none +end program main