diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37f220e2b1..d187f6943b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,7 @@ jobs: image-version: [22.04] build-type: [Release, Debug] sanitizers: [ON, OFF] + features: ["nosarif", "sarif"] runs-on: ubuntu-${{ matrix.image-version }} timeout-minutes: 60 @@ -49,10 +50,11 @@ jobs: fetch-depth: 1 - name: Configure build - sanitizers ${{ matrix.sanitizers }} - run: cmake --preset ci + run: cmake --preset ci-${{ matrix.features }} - name: Build ${{ matrix.build-type }} with sanitizers set ${{ matrix.sanitizers }} - run: cmake --build --preset ci --config ${{ matrix.build-type }} -j $(nproc) + run: cmake --build --preset ci-${{ matrix.features }} --config ${{ matrix.build-type }} -j $(nproc) - name: Test ${{ matrix.build-type }} with sanitizers set ${{ matrix.sanitizers }} - run: ctest --preset ci --build-config ${{ matrix.build-type }} + run: ctest --preset ci-${{ matrix.features }} --build-config ${{ matrix.build-type }} + diff --git a/CMakeLists.txt b/CMakeLists.txt index 7505a98860..fedde7a808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,6 +305,20 @@ target_link_libraries(vast_settings "$" ) +if (sarif IN_LIST VCPKG_MANIFEST_FEATURES) + option(VAST_ENABLE_SARIF "Enable SARIF export" ON) +else() + option(VAST_ENABLE_SARIF "Enable SARIF export" OFF) +endif() + +if (VAST_ENABLE_SARIF) + target_link_libraries(vast_settings + INTERFACE + "$" + ) + target_compile_definitions(vast_settings INTERFACE VAST_ENABLE_SARIF) +endif() + # # VAST libraries # diff --git a/CMakePresets.json b/CMakePresets.json index f34564dcb2..95b7ecd12c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -30,6 +30,19 @@ "CMAKE_VERBOSE_MAKEFILE": "True" } }, + { + "name": "ci-nosarif", + "displayName": "Configure VAST for CI withour SARIF support", + "inherits": "ci" + }, + { + "name": "ci-sarif", + "displayName": "Configure VAST for CI withour SARIF support", + "inherits": "ci", + "cacheVariables": { + "VCPKG_MANIFEST_FEATURES": "sarif" + } + }, { "name": "compiler-explorer", "displayName": "Configure VAST for Compiler Explorer", @@ -85,8 +98,12 @@ "configuration": "RelWithDebInfo" }, { - "name": "ci", - "configurePreset": "ci" + "name": "ci-nosarif", + "configurePreset": "ci-nosarif" + }, + { + "name": "ci-sarif", + "configurePreset": "ci-sarif" }, { "name": "ci-release", @@ -157,8 +174,13 @@ "configuration": "RelWithDebInfo" }, { - "name": "ci", - "configurePreset": "ci", + "name": "ci-nosarif", + "configurePreset": "ci-nosarif", + "inherits": "test-base" + }, + { + "name": "ci-sarif", + "configurePreset": "ci-sarif", "inherits": "test-base" } ], diff --git a/docs/Tools/vast-front.md b/docs/Tools/vast-front.md index f9cdba8be6..ba7433ef7c 100644 --- a/docs/Tools/vast-front.md +++ b/docs/Tools/vast-front.md @@ -54,6 +54,9 @@ Additional customization options include: - After each pass that was specified as an option store MLIR into a file (format is `src.pass_name`). - `"*"` stores snapshot after every conversion. +- `-vast-output-sarif="report.sarif"` + - Outputs diagnostics as a SARIF report file. + ## Pipelines WIP pipelines documentation diff --git a/include/vast/Analysis/Sarif/Sarif.hpp b/include/vast/Analysis/Sarif/Sarif.hpp new file mode 100644 index 0000000000..ad9c14c03e --- /dev/null +++ b/include/vast/Analysis/Sarif/Sarif.hpp @@ -0,0 +1,34 @@ +// Copyright (c) 2024-present, Trail of Bits, Inc. + +#pragma once + +#ifdef VAST_ENABLE_SARIF + #include + + #include + +namespace vast::analysis::sarif { + + class sarif_analysis + { + std::vector< gap::sarif::result > sarif_results; + + public: + virtual ~sarif_analysis() = default; + + const std::vector< gap::sarif::result > &results() const noexcept { + return sarif_results; + } + + protected: + void append_result(const gap::sarif::result &result) { + sarif_results.push_back(result); + } + + void append_result(gap::sarif::result &&result) { + sarif_results.emplace_back(std::move(result)); + } + }; + +} // namespace vast::analysis::sarif +#endif // VAST_ENABLE_SARIF diff --git a/include/vast/Config/config.h.cmake b/include/vast/Config/config.h.cmake index 49d241f29f..8583957651 100644 --- a/include/vast/Config/config.h.cmake +++ b/include/vast/Config/config.h.cmake @@ -19,6 +19,8 @@ namespace vast { constexpr std::string_view version = "${VAST_VERSION}"; + constexpr std::string_view homepage_url = "${PROJECT_HOMEPAGE_URL}"; + constexpr std::string_view bug_report_url = "${BUG_REPORT_URL}"; constexpr std::string_view default_resource_dir = "${VAST_DEFAULT_RESOURCE_DIR}"; diff --git a/include/vast/Frontend/Options.hpp b/include/vast/Frontend/Options.hpp index 53f88ee338..80335801a7 100644 --- a/include/vast/Frontend/Options.hpp +++ b/include/vast/Frontend/Options.hpp @@ -100,6 +100,8 @@ namespace vast::cc constexpr option_t vast_verify_diags = "verify-diags"; constexpr option_t disable_emit_cxx_default = "disable-emit-cxx-default"; + constexpr option_t output_sarif = "output-sarif"; + bool emit_only_mlir(const vast_args &vargs); bool emit_only_llvm(const vast_args &vargs); } // namespace opt diff --git a/include/vast/Frontend/Sarif.hpp b/include/vast/Frontend/Sarif.hpp new file mode 100644 index 0000000000..76f535ce0b --- /dev/null +++ b/include/vast/Frontend/Sarif.hpp @@ -0,0 +1,53 @@ +// Copyright (c) 2024, Trail of Bits, Inc. + +#pragma once + + +#ifdef VAST_ENABLE_SARIF + #include + + #include "vast/Frontend/Options.hpp" + #include "vast/Util/Common.hpp" + +namespace vast::cc::sarif { + + gap::sarif::location mk_location(loc_t loc); + gap::sarif::location mk_location(file_loc_t loc); + gap::sarif::location mk_location(name_loc_t loc); + + gap::sarif::physical_location get_physical_loc(file_loc_t loc); + + gap::sarif::level get_severity_level(mlir::DiagnosticSeverity severity); + + struct diagnostics { + gap::sarif::run run; + + explicit diagnostics(const vast_args &vargs); + + auto handler() { + return [&] (auto &diag) { + gap::sarif::result result = { + .ruleId = "mlir-diag", + .message = { .text = diag.str() } + }; + + if (auto loc = mk_location(diag.getLocation()); loc.physicalLocation.has_value()) { + result.locations.push_back(std::move(loc)); + } + + result.level = get_severity_level(diag.getSeverity()); + run.results.push_back(std::move(result)); + }; + }; + + gap::sarif::root emit(logical_result result) && { + run.invocations[0].executionSuccessful = mlir::succeeded(result); + return { + .version = gap::sarif::version::k2_1_0, + .runs{ std::move(run) }, + }; + } + }; + +} // namespace vast::cc::sarif +#endif // VAST_ENABLE_SARIF diff --git a/include/vast/Util/Common.hpp b/include/vast/Util/Common.hpp index 52700fa433..f73a7823e0 100644 --- a/include/vast/Util/Common.hpp +++ b/include/vast/Util/Common.hpp @@ -74,7 +74,9 @@ namespace vast { using values_t = mlir::SmallVector< mlir_value >; - using loc_t = mlir::Location; + using loc_t = mlir::Location; + using name_loc_t = mlir::NameLoc; + using file_loc_t = mlir::FileLineColLoc; using mlir_builder = mlir::OpBuilder; using op_state = mlir::OperationState; diff --git a/lib/vast/Frontend/CMakeLists.txt b/lib/vast/Frontend/CMakeLists.txt index cc99558040..d6817e7a90 100644 --- a/lib/vast/Frontend/CMakeLists.txt +++ b/lib/vast/Frontend/CMakeLists.txt @@ -5,6 +5,7 @@ add_vast_library(Frontend Consumer.cpp Options.cpp Pipelines.cpp + Sarif.cpp Targets.cpp LINK_LIBS PUBLIC diff --git a/lib/vast/Frontend/Consumer.cpp b/lib/vast/Frontend/Consumer.cpp index bcec445d1a..49070f32fb 100644 --- a/lib/vast/Frontend/Consumer.cpp +++ b/lib/vast/Frontend/Consumer.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2023-present, Trail of Bits, Inc. #include "vast/Frontend/Consumer.hpp" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Location.h" VAST_RELAX_WARNINGS #include @@ -17,18 +19,22 @@ VAST_RELAX_WARNINGS VAST_UNRELAX_WARNINGS #include +#include #include "vast/CodeGen/CodeGenDriver.hpp" #include "vast/Util/Common.hpp" #include "vast/Frontend/Pipelines.hpp" +#include "vast/Frontend/Sarif.hpp" #include "vast/Frontend/Targets.hpp" #include "vast/Target/LLVMIR/Convert.hpp" #include "vast/Dialect/Core/CoreOps.hpp" +#include "vast/Config/config.h" + namespace vast::cc { [[nodiscard]] target_dialect parse_target_dialect(string_ref from); @@ -101,9 +107,7 @@ namespace vast::cc { void vast_consumer::HandleVTable(clang::CXXRecordDecl * /* decl */) { VAST_UNIMPLEMENTED; } - owning_mlir_module_ref vast_consumer::result() { - return driver->freeze(); - } + owning_mlir_module_ref vast_consumer::result() { return driver->freeze(); } // // vast stream consumer @@ -153,8 +157,8 @@ namespace vast::cc { process_mlir_module(target_dialect::llvm, mod.get()); auto final_mlir_module = mlir::cast< mlir_module >(mod->getBody()->front()); - auto llvm_mod = target::llvmir::translate(final_mlir_module, llvm_context); - auto dl = driver->acontext().getTargetInfo().getDataLayoutString(); + auto llvm_mod = target::llvmir::translate(final_mlir_module, llvm_context); + auto dl = driver->acontext().getTargetInfo().getDataLayoutString(); clang::EmitBackendOutput( opts.diags, opts.headers, opts.codegen, opts.target, opts.lang, dl, llvm_mod.get(), @@ -162,9 +166,42 @@ namespace vast::cc { ); } - void vast_stream_consumer::process_mlir_module( - target_dialect target, mlir_module mod + namespace sarif { + struct diagnostics; + } // namespace sarif + + #ifdef VAST_ENABLE_SARIF + std::unique_ptr< vast::cc::sarif::diagnostics > setup_sarif_diagnostics( + const vast_args &vargs, mcontext_t &mctx ) { + if (vargs.get_option(opt::output_sarif)) { + auto diags = std::make_unique< vast::cc::sarif::diagnostics >(vargs); + mctx.getDiagEngine().registerHandler(diags->handler()); + return diags; + } else { + return nullptr; + } + } + #endif // VAST_ENABLE_SARIF + + void emit_sarif_diagnostics( + vast::cc::sarif::diagnostics &&sarif_diagnostics, logical_result result, string_ref path + ) { + #ifdef VAST_ENABLE_SARIF + std::error_code ec; + llvm::raw_fd_ostream os(path, ec, llvm::sys::fs::OF_None); + if (ec) { + VAST_FATAL("Failed to open file for SARIF output: {}", ec.message()); + } + + nlohmann::json report = std::move(sarif_diagnostics).emit(result); + os << report.dump(2); + #else + VAST_REPORT("SARIF support is disabled"); + #endif // VAST_ENABLE_SARIF + } + + void vast_stream_consumer::process_mlir_module(target_dialect target, mlir_module mod) { // Handle source manager properly given that lifetime analysis // might emit warnings and remarks. auto &src_mgr = driver->acontext().getSourceManager(); @@ -196,8 +233,15 @@ namespace vast::cc { auto pipeline = setup_pipeline(pipeline_source::ast, target, mctx, vargs, snapshot_prefix); VAST_CHECK(pipeline, "failed to setup pipeline"); + #ifdef VAST_ENABLE_SARIF + auto sarif_diagnostics = setup_sarif_diagnostics(vargs, mctx); + #endif // VAST_ENABLE_SARIF + auto result = pipeline->run(mod); - VAST_CHECK(mlir::succeeded(result), "MLIR pass manager failed when running vast passes"); + + VAST_CHECK( + mlir::succeeded(result), "MLIR pass manager failed when running vast passes" + ); // Verify the diagnostic handler to make sure that each of the // diagnostics matched. @@ -206,6 +250,20 @@ namespace vast::cc { VAST_FATAL("failed mlir codegen"); } + if (auto path = vargs.get_option(opt::output_sarif)) { + #ifdef VAST_ENABLE_SARIF + if (sarif_diagnostics) { + emit_sarif_diagnostics( + std::move(*sarif_diagnostics), result, path.value().str() + ); + } else { + VAST_REPORT("SARIF diagnostics are missing"); + } + #else + VAST_REPORT("SARIF support is disabled"); + #endif // VAST_ENABLE_SARIF + } + // Emit remaining defaulted C++ methods // if (!vargs.has_option(opt::disable_emit_cxx_default)) { // generator->build_default_methods(); diff --git a/lib/vast/Frontend/Sarif.cpp b/lib/vast/Frontend/Sarif.cpp new file mode 100644 index 0000000000..4ac949ac0a --- /dev/null +++ b/lib/vast/Frontend/Sarif.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2024, Trail of Bits, Inc. + +#include "vast/Frontend/Sarif.hpp" + +#ifdef VAST_ENABLE_SARIF + +#include "vast/Config/config.h" + +namespace vast::cc::sarif { + + gap::sarif::location mk_location(loc_t loc) { + if (auto file_loc = mlir::dyn_cast< file_loc_t >(loc)) { + return mk_location(file_loc); + } else if (auto name_loc = mlir::dyn_cast< name_loc_t >(loc)) { + return mk_location(name_loc); + } + + return {}; + } + + gap::sarif::location mk_location(file_loc_t loc) { + return { + .physicalLocation{ get_physical_loc(loc) }, + }; + } + + gap::sarif::location mk_location(name_loc_t loc) { + return { + .physicalLocation{ + get_physical_loc(mlir::cast< file_loc_t >(loc.getChildLoc())) + }, + .logicalLocations{ { .name = loc.getName().str() } }, + }; + } + + gap::sarif::physical_location get_physical_loc(file_loc_t loc) { + std::filesystem::path file_path{ loc.getFilename().str() }; + auto abs_path = std::filesystem::absolute(file_path); + return { + .artifactLocation{ { .uri{ "file://" + abs_path.string() } } }, + .region{ { + .startLine = loc.getLine(), + .startColumn = loc.getColumn(), + } }, + }; + } + + gap::sarif::level get_severity_level(mlir::DiagnosticSeverity severity) { + using enum gap::sarif::level; + using enum mlir::DiagnosticSeverity; + switch (severity) { + case Note: return kNote; + case Warning: return kWarning; + case Error: return kError; + case Remark: return kNote; + } + } + + diagnostics::diagnostics(const vast_args &vargs) + : run(gap::sarif::run{ + .tool{ + .driver{ + .name = "vast-front", + .organization = "Trail of Bits, inc.", + .product = "VAST", + .version = std::string{ vast::version }, + .informationUri = std::string{ vast::homepage_url }, + }, + }, + .invocations{{ + .arguments{ std::begin(vargs.args), std::end(vargs.args) }, + .executionSuccessful = true, + }} + }) + {} + +} // namespace vast::cc::sarif + +#endif // VAST_ENABLE_SARIF diff --git a/ports/gap/portfile.cmake b/ports/gap/portfile.cmake index 946dab766b..91b138f2a4 100644 --- a/ports/gap/portfile.cmake +++ b/ports/gap/portfile.cmake @@ -1,8 +1,8 @@ vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO lifting-bits/gap - REF 53b1a07b1f80036774c747266c23fd703c91228c - SHA512 d231f7029a5d91e880bcb81b036725ad109c56965f096a9795c302ce755911f44c80fc2cfe6424c502f0b4d79a1f7dd66757eab668f20d67d4cc236d1e785737 + REF a7ceecc118410f11b0c904b88a5ccfd6221f434b + SHA512 20b72abb4a5c6bb97a218ea0c9492c1e70ba158c4a351656fec29141ed15dd17cdbececa0b31ebb7d4d44af6db69be4c4d40ef9ebd19dd8ac3a085e2e5ca15a4 HEAD_REF main ) diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 4e1556ba8b..561f6d8d4e 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -156,6 +156,9 @@ if stdbit_test.returncode == 0: config.available_features.add("stdbit") +if config.enable_sarif: + config.available_features.add("sarif") + uchar_input = b''' #include diff --git a/test/lit.site.cfg.py.in b/test/lit.site.cfg.py.in index ff9de8a898..a7d602a5bc 100644 --- a/test/lit.site.cfg.py.in +++ b/test/lit.site.cfg.py.in @@ -2,6 +2,9 @@ import sys +def to_bool(value): + return value in [True, 'TRUE', 'ON', 1, '1'] + config.host_triple = "@LLVM_HOST_TRIPLE@" config.target_triple = "@TARGET_TRIPLE@" config.llvm_src_root = "@LLVM_SOURCE_DIR@" @@ -31,6 +34,7 @@ config.llvm_host_triple = '@LLVM_HOST_TRIPLE@' config.host_arch = "@HOST_ARCH@" config.vast_src_root = "@CMAKE_SOURCE_DIR@" config.vast_obj_root = "@CMAKE_BINARY_DIR@" +config.enable_sarif = to_bool("@VAST_ENABLE_SARIF@") # Support substitution of the tools_dir with user parameters. This is # used when we can't determine the tool dir at configuration time. diff --git a/test/vast/Compile/Sarif/argc.c b/test/vast/Compile/Sarif/argc.c new file mode 100644 index 0000000000..786cdd2ce3 --- /dev/null +++ b/test/vast/Compile/Sarif/argc.c @@ -0,0 +1,12 @@ +// RUN: %vast-front -o %t -vast-output-sarif=%t.sarif %s && test -f %t.sarif && cat %t.sarif | %file-check %s +// REQUIRES: sarif + +// CHECK: "informationUri": "https://github.com/trailofbits/vast.git", +// CHECK: "name": "vast-front", +// CHECK: "organization": "Trail of Bits, inc.", +// CHECK: "product": "VAST", + +int main(int argc, char **argv) +{ + return argc; +} diff --git a/vcpkg.json b/vcpkg.json index 62a6aaf172..046e6560fe 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,5 +1,18 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", + "features": { + "sarif": { + "description": "Enables SARIF output support", + "dependencies": [ + { + "name": "gap", + "features": [ + "sarif" + ] + } + ] + } + }, "dependencies": [ "gap" ]