From 59619fa8eb4326b9c1a14788e3b611de2db1205d Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Thu, 24 Jul 2025 13:00:41 -0700 Subject: [PATCH] Make driver fuzzing more robust for clang flags (#5845) I'm not sure the target in use here will reliably crash over time, but it does right now, and that seems reasonable...? Example crash: ``` file_test: external/+llvm_project+llvm-project/clang/lib/Driver/ToolChains/Darwin.h:505: bool clang::driver::toolchains::Darwin::isTargetWatchOSBased() const: Assertion `TargetInitialized && "Target not initialized!"' failed. ``` Stack fragment: ``` ... #10 0x0000562ba07dec33 isTargetWatchOSBased /proc/self/cwd/external/+llvm_project+llvm-project/clang/lib/Driver/ToolChains/Darwin.h:505:5 #11 0x0000562ba07dec33 clang::driver::toolchains::DarwinClang::addClangWarningOptions(llvm::SmallVector&) const /proc/self/cwd/external/+llvm_project+llvm-project/clang/lib/Driver/ToolChains/Darwin.cpp:1188:7 #12 0x0000562ba072afc7 clang::driver::tools::Clang::ConstructJob(clang::driver::Compilation&, clang::driver::JobAction const&, clang::driver::InputInfo const&, llvm::SmallVector const&, llvm::opt::ArgList const&, char const*) const /proc/self/cwd/external/+llvm_project+llvm-project/clang/lib/Driver/ToolChains/Clang.cpp:0:6 #13 0x0000562ba06306d8 clang::driver::Driver::BuildJobsForActionNoCache(clang::driver::Compilation&, clang::driver::Action const*, clang::driver::ToolChain const*, llvm::StringRef, bool, bool, char const*, std::__1::map, std::__1::allocator>>, llvm::SmallVector, std::__1::less, std::__1::allocator>>>, std::__1::allocator, std::__1::allocator>> const, llvm::SmallVector>>>&, clang::driver::Action::OffloadKind) const /proc/self/cwd/external/+llvm_project+llvm-project/clang/lib/Driver/Driver.cpp:6083:10 ... #28 0x0000562b9e479d1f Carbon::BuildClangInvocation(Carbon::Diagnostics::Consumer&, llvm::IntrusiveRefCntPtr, llvm::ArrayRef, std::__1::allocator>>) /proc/self/cwd/toolchain/base/clang_invocation.cpp:103:21 ... ``` --------- Co-authored-by: Chandler Carruth --- testing/base/BUILD | 1 + toolchain/codegen/BUILD | 1 + toolchain/codegen/codegen.cpp | 6 ++---- toolchain/diagnostics/diagnostic_kind.def | 1 + toolchain/driver/BUILD | 4 ++++ toolchain/driver/compile_subcommand.cpp | 18 ++++++++++++++++++ .../testdata/compile/fail_clang_args.carbon | 4 ++-- .../fail_fuzzing_invalid_clang_arg.carbon | 15 +++++++++++++++ .../fail_fuzzing_invalid_target.carbon | 15 +++++++++++++++ toolchain/sem_ir/BUILD | 1 + 10 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 toolchain/driver/testdata/fail_fuzzing_invalid_clang_arg.carbon create mode 100644 toolchain/driver/testdata/fail_fuzzing_invalid_target.carbon diff --git a/testing/base/BUILD b/testing/base/BUILD index 81c7ba6bd928..305c8b28a0b9 100644 --- a/testing/base/BUILD +++ b/testing/base/BUILD @@ -80,6 +80,7 @@ cc_test( ":global_exe_path", ":gtest_main", ":source_gen_lib", + "//common:all_llvm_targets", "//common:set", "//toolchain/driver", "//toolchain/install:install_paths_test_helpers", diff --git a/toolchain/codegen/BUILD b/toolchain/codegen/BUILD index e02f7d7989c7..815916ca6c07 100644 --- a/toolchain/codegen/BUILD +++ b/toolchain/codegen/BUILD @@ -16,6 +16,7 @@ cc_library( srcs = ["codegen.cpp"], hdrs = ["codegen.h"], deps = [ + "//common:check", "@llvm-project//llvm:Core", "@llvm-project//llvm:MC", "@llvm-project//llvm:Support", diff --git a/toolchain/codegen/codegen.cpp b/toolchain/codegen/codegen.cpp index 9bfbacea8f19..a5f1f36f6faf 100644 --- a/toolchain/codegen/codegen.cpp +++ b/toolchain/codegen/codegen.cpp @@ -8,6 +8,7 @@ #include #include +#include "common/check.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Target/TargetOptions.h" @@ -20,11 +21,8 @@ auto CodeGen::Make(llvm::Module* module, llvm::StringRef target_triple_str, std::string error; const llvm::Target* target = llvm::TargetRegistry::lookupTarget(target_triple_str, error); + CARBON_CHECK(target, "Target should be validated before codegen"); - if (!target) { - *errors << "error: invalid target: " << error << "\n"; - return {}; - } llvm::Triple target_triple(target_triple_str); module->setTargetTriple(target_triple); diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index bdf7e04699b0..d518cc5c1d51 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -27,6 +27,7 @@ CARBON_DIAGNOSTIC_KIND(CompilePhaseFlagConflict) CARBON_DIAGNOSTIC_KIND(CompilePreludeManifestError) CARBON_DIAGNOSTIC_KIND(CompileInputNotRegularFile) CARBON_DIAGNOSTIC_KIND(CompileOutputFileOpenError) +CARBON_DIAGNOSTIC_KIND(CompileTargetInvalid) CARBON_DIAGNOSTIC_KIND(FormatMultipleFilesToOneOutput) CARBON_DIAGNOSTIC_KIND(ToolFuzzingDisallowed) diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 7d7a7d75ea63..4932b695838d 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -4,6 +4,7 @@ load("@rules_shell//shell:sh_test.bzl", "sh_test") load("//bazel/cc_rules:defs.bzl", "cc_binary", "cc_library", "cc_test") +load("//bazel/cc_toolchains:defs.bzl", "cc_env") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") package(default_visibility = ["//visibility:public"]) @@ -62,6 +63,7 @@ cc_binary( srcs = ["compile_benchmark.cpp"], deps = [ ":driver", + "//common:all_llvm_targets", "//testing/base:benchmark_main", "//testing/base:global_exe_path", "//testing/base:source_gen_lib", @@ -81,6 +83,7 @@ sh_test( # The `$$` is repeated for Bazel escaping of `$`. "--benchmark_filter=/256$$", ], + env = cc_env(), ) cc_library( @@ -144,6 +147,7 @@ cc_library( "//toolchain/sem_ir:typed_insts", "//toolchain/source:source_buffer", "@llvm-project//llvm:Core", + "@llvm-project//llvm:MC", "@llvm-project//llvm:Support", "@llvm-project//llvm:TargetParser", ], diff --git a/toolchain/driver/compile_subcommand.cpp b/toolchain/driver/compile_subcommand.cpp index b2a7acc58ca4..ee28f7810963 100644 --- a/toolchain/driver/compile_subcommand.cpp +++ b/toolchain/driver/compile_subcommand.cpp @@ -15,6 +15,7 @@ #include "common/vlog.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/MC/TargetRegistry.h" #include "toolchain/base/clang_invocation.h" #include "toolchain/base/timings.h" #include "toolchain/check/check.h" @@ -844,6 +845,17 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult { return {.success = false}; } + // Validate the target before passing it to Clang. + std::string target_error; + const llvm::Target* target = llvm::TargetRegistry::lookupTarget( + options_.codegen_options.target, target_error); + if (!target) { + CARBON_DIAGNOSTIC(CompileTargetInvalid, Error, "invalid target: {0}", + std::string); + driver_env.emitter.Emit(CompileTargetInvalid, target_error); + return {.success = false}; + } + std::shared_ptr clang_invocation; // Build a clang invocation. We do this regardless of whether we're running // check, because this is essentially performing further option validation, @@ -862,6 +874,12 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult { // TODO: Decide if we want this. "-fPIE", }; + if (driver_env.fuzzing && !options_.clang_args.empty()) { + // Parsing specific Clang arguments can reach deep into + // external libraries that aren't fuzz clean. + DisableFuzzingExternalLibraries(driver_env, "compile"); + return {.success = false}; + } for (auto str : options_.clang_args) { clang_path_and_args.push_back(str.str()); } diff --git a/toolchain/driver/testdata/compile/fail_clang_args.carbon b/toolchain/driver/testdata/compile/fail_clang_args.carbon index aa39ff6003a4..4fa9f48f16fd 100644 --- a/toolchain/driver/testdata/compile/fail_clang_args.carbon +++ b/toolchain/driver/testdata/compile/fail_clang_args.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// ARGS: --include-diagnostic-kind compile --target=x86-pc-linux-gnu --clang-arg=-Wall --clang-arg=-Wextra foo.carbon -- -Wuninitialized -Wno-all -### +// ARGS: --include-diagnostic-kind compile --target=x86_64-unknown-linux-gnu --clang-arg=-Wall --clang-arg=-Wextra foo.carbon -- -Wuninitialized -Wno-all -### // // SET-CAPTURE-CONSOLE-OUTPUT // SET-CHECK-SUBSET @@ -13,6 +13,6 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/compile/fail_clang_args.carbon // CHECK:STDERR: {{.*}}clang version {{.*}} // CHECK:STDERR: InstalledDir: {{.*}}/toolchain/install/prefix_root/lib/carbon/../../lib/carbon/llvm/bin -// CHECK:STDERR: "{{.*}}/toolchain/install/prefix_root/lib/carbon/../../lib/carbon/llvm/bin/clang" "-cc1" {{.*}}"-triple" "x86-pc-linux-gnu" {{.*}}"-fsyntax-only" {{.*}} "-resource-dir" {{.*}} "-Wall" "-Wextra" "-Wuninitialized" "-Wno-all" {{.*}} +// CHECK:STDERR: "{{.*}}/toolchain/install/prefix_root/lib/carbon/../../lib/carbon/llvm/bin/clang" "-cc1" {{.*}}"-triple" "x86_64-unknown-linux-gnu" {{.*}}"-fsyntax-only" {{.*}} "-resource-dir" {{.*}} "-Wall" "-Wextra" "-Wuninitialized" "-Wno-all" {{.*}} // --- foo.carbon diff --git a/toolchain/driver/testdata/fail_fuzzing_invalid_clang_arg.carbon b/toolchain/driver/testdata/fail_fuzzing_invalid_clang_arg.carbon new file mode 100644 index 000000000000..98ceb17c8755 --- /dev/null +++ b/toolchain/driver/testdata/fail_fuzzing_invalid_clang_arg.carbon @@ -0,0 +1,15 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// ARGS: --include-diagnostic-kind --fuzzing compile nonexistent -- --target=mips64-a-ios +// +// SET-CAPTURE-CONSOLE-OUTPUT +// clang-format off +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/fail_fuzzing_invalid_clang_arg.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/fail_fuzzing_invalid_clang_arg.carbon +// CHECK:STDERR: error: preventing fuzzing of `compile` subcommand due to external library [ToolFuzzingDisallowed] +// CHECK:STDERR: diff --git a/toolchain/driver/testdata/fail_fuzzing_invalid_target.carbon b/toolchain/driver/testdata/fail_fuzzing_invalid_target.carbon new file mode 100644 index 000000000000..389d2797816c --- /dev/null +++ b/toolchain/driver/testdata/fail_fuzzing_invalid_target.carbon @@ -0,0 +1,15 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// ARGS: --include-diagnostic-kind --fuzzing compile nonexistent --target=mips64-a-ios +// +// SET-CAPTURE-CONSOLE-OUTPUT +// clang-format off +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/fail_fuzzing_invalid_target.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/fail_fuzzing_invalid_target.carbon +// CHECK:STDERR: error: invalid target: No available targets are compatible with triple "mips64-a-ios" [CompileTargetInvalid] +// CHECK:STDERR: diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 74bef33808ce..584bf238b0f0 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -292,6 +292,7 @@ cc_test( size = "small", srcs = ["yaml_test.cpp"], deps = [ + "//common:all_llvm_targets", "//common:ostream", "//common:raw_string_ostream", "//testing/base:global_exe_path",