CFI: Fix error compiling core with LLVM CFI enabled

Fix #90546 by filtering out global value function pointer types from the
type tests, and adding the LowerTypeTests pass to the rustc LTO
optimization pipelines.
This commit is contained in:
Ramon de C Valle 2023-07-11 16:19:42 -07:00
parent 3c554f5cb4
commit f837c48f0d
14 changed files with 90 additions and 31 deletions

View File

@ -449,6 +449,8 @@ pub(crate) unsafe fn llvm_optimize(
Some(llvm::SanitizerOptions {
sanitize_address: config.sanitizer.contains(SanitizerSet::ADDRESS),
sanitize_address_recover: config.sanitizer_recover.contains(SanitizerSet::ADDRESS),
sanitize_cfi: config.sanitizer.contains(SanitizerSet::CFI),
sanitize_kcfi: config.sanitizer.contains(SanitizerSet::KCFI),
sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY),
sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY),
sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int,
@ -484,6 +486,7 @@ pub(crate) unsafe fn llvm_optimize(
&*module.module_llvm.tm,
to_pass_builder_opt_level(opt_level),
opt_stage,
cgcx.opts.cg.linker_plugin_lto.enabled(),
config.no_prepopulate_passes,
config.verify_llvm_ir,
using_thin_buffers,

View File

@ -1525,9 +1525,9 @@ fn cfi_type_test(
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
) {
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
if is_indirect_call && fn_abi.is_some() && self.tcx.sess.is_sanitizer_cfi_enabled() {
if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::CFI) {
let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) };
if self.tcx.sess.is_sanitizer_cfi_enabled() && let Some(fn_abi) = fn_abi && is_indirect_call {
if let Some(fn_attrs) = fn_attrs && fn_attrs.no_sanitize.contains(SanitizerSet::CFI) {
return;
}
@ -1539,7 +1539,7 @@ fn cfi_type_test(
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
}
let typeid = typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
let typeid = typeid_for_fnabi(self.tcx, fn_abi, options);
let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
// Test whether the function pointer is associated with the type identifier.
@ -1563,9 +1563,10 @@ fn kcfi_operand_bundle(
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
) -> Option<llvm::OperandBundleDef<'ll>> {
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
let kcfi_bundle = if is_indirect_call && self.tcx.sess.is_sanitizer_kcfi_enabled() {
if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::KCFI) {
let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) };
let kcfi_bundle =
if self.tcx.sess.is_sanitizer_kcfi_enabled() && let Some(fn_abi) = fn_abi && is_indirect_call {
if let Some(fn_attrs) = fn_attrs && fn_attrs.no_sanitize.contains(SanitizerSet::KCFI) {
return None;
}
@ -1577,7 +1578,7 @@ fn kcfi_operand_bundle(
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
}
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options);
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
} else {
None

View File

@ -477,6 +477,8 @@ pub enum OptStage {
pub struct SanitizerOptions {
pub sanitize_address: bool,
pub sanitize_address_recover: bool,
pub sanitize_cfi: bool,
pub sanitize_kcfi: bool,
pub sanitize_memory: bool,
pub sanitize_memory_recover: bool,
pub sanitize_memory_track_origins: c_int,
@ -1083,6 +1085,7 @@ pub fn LLVMStructTypeInContext<'a>(
pub fn LLVMRustGlobalAddMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
pub fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
pub fn LLVMIsAFunction(Val: &Value) -> Option<&Value>;
pub fn LLVMRustIsNonGVFunctionPointerTy(Val: &Value) -> bool;
// Operations on constants of any type
pub fn LLVMConstNull(Ty: &Type) -> &Value;
@ -2319,6 +2322,7 @@ pub fn LLVMRustOptimize<'a>(
TM: &'a TargetMachine,
OptLevel: PassBuilderOptLevel,
OptStage: OptStage,
IsLinkerPluginLTO: bool,
NoPrepopulatePasses: bool,
VerifyIR: bool,
UseThinLTOBuffers: bool,

View File

@ -29,6 +29,7 @@
#include "llvm/Transforms/IPO/AlwaysInliner.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/IPO/LowerTypeTests.h"
#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h"
#include "llvm/Transforms/Utils/AddDiscriminators.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
@ -599,6 +600,8 @@ enum class LLVMRustOptStage {
struct LLVMRustSanitizerOptions {
bool SanitizeAddress;
bool SanitizeAddressRecover;
bool SanitizeCFI;
bool SanitizeKCFI;
bool SanitizeMemory;
bool SanitizeMemoryRecover;
int SanitizeMemoryTrackOrigins;
@ -615,6 +618,7 @@ LLVMRustOptimize(
LLVMTargetMachineRef TMRef,
LLVMRustPassBuilderOptLevel OptLevelRust,
LLVMRustOptStage OptStage,
bool IsLinkerPluginLTO,
bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers,
bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize,
bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
@ -722,6 +726,18 @@ LLVMRustOptimize(
std::vector<std::function<void(ModulePassManager &, OptimizationLevel)>>
OptimizerLastEPCallbacks;
if (!IsLinkerPluginLTO
&& SanitizerOptions && SanitizerOptions->SanitizeCFI
&& !NoPrepopulatePasses) {
PipelineStartEPCallbacks.push_back(
[](ModulePassManager &MPM, OptimizationLevel Level) {
MPM.addPass(LowerTypeTestsPass(/*ExportSummary=*/nullptr,
/*ImportSummary=*/nullptr,
/*DropTypeTests=*/false));
}
);
}
if (VerifyIR) {
PipelineStartEPCallbacks.push_back(
[VerifyIR](ModulePassManager &MPM, OptimizationLevel Level) {

View File

@ -1952,3 +1952,14 @@ extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) {
extern "C" bool LLVMRustIsBitcode(char *ptr, size_t len) {
return identify_magic(StringRef(ptr, len)) == file_magic::bitcode;
}
extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) {
if (unwrap<Value>(V)->getType()->isPointerTy()) {
if (auto *GV = dyn_cast<GlobalValue>(unwrap<Value>(V))) {
if (GV->getValueType()->isFunctionTy())
return false;
}
return true;
}
return false;
}

View File

@ -86,7 +86,9 @@ session_sanitizer_cfi_generalize_pointers_requires_cfi = `-Zsanitizer-cfi-genera
session_sanitizer_cfi_normalize_integers_requires_cfi = `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`
session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`
session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`
session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`
session_sanitizer_not_supported = {$us} sanitizer is not supported for this target

View File

@ -114,6 +114,10 @@ pub struct CannotMixAndMatchSanitizers {
#[diag(session_sanitizer_cfi_requires_lto)]
pub struct SanitizerCfiRequiresLto;
#[derive(Diagnostic)]
#[diag(session_sanitizer_cfi_requires_single_codegen_unit)]
pub struct SanitizerCfiRequiresSingleCodegenUnit;
#[derive(Diagnostic)]
#[diag(session_sanitizer_cfi_canonical_jump_tables_requires_cfi)]
pub struct SanitizerCfiCanonicalJumpTablesRequiresCfi;

View File

@ -1616,13 +1616,19 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
// LLVM CFI requires LTO.
if sess.is_sanitizer_cfi_enabled()
&& !(sess.lto() == config::Lto::Fat
|| sess.lto() == config::Lto::Thin
|| sess.opts.cg.linker_plugin_lto.enabled())
&& !(sess.lto() == config::Lto::Fat || sess.opts.cg.linker_plugin_lto.enabled())
{
sess.emit_err(errors::SanitizerCfiRequiresLto);
}
// LLVM CFI using rustc LTO requires a single codegen unit.
if sess.is_sanitizer_cfi_enabled()
&& sess.lto() == config::Lto::Fat
&& !(sess.codegen_units().as_usize() == 1)
{
sess.emit_err(errors::SanitizerCfiRequiresSingleCodegenUnit);
}
// LLVM CFI is incompatible with LLVM KCFI.
if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() {
sess.emit_err(errors::CannotMixAndMatchSanitizers {

View File

@ -1,6 +1,6 @@
// run-pass
// build-pass
// needs-sanitizer-cfi
// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi
// compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi
// no-prefer-dynamic
// only-x86_64-unknown-linux-gnu

View File

@ -2,10 +2,10 @@
// encode_ty and caused the compiler to ICE.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi --edition=2021
// compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi --edition=2021
// no-prefer-dynamic
// only-x86_64-unknown-linux-gnu
// run-pass
// build-pass
use std::future::Future;

View File

@ -1,4 +1,4 @@
// Verifies that `-Zsanitizer=cfi` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`.
// Verifies that `-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`.
//
// needs-sanitizer-cfi
// compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi

View File

@ -1,4 +1,4 @@
error: `-Zsanitizer=cfi` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`
error: `-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
// Verifies that `-Zsanitizer=cfi` with `-Clto` or `-Clto=thin` requires `-Ccodegen-units=1`.
//
// needs-sanitizer-cfi
// compile-flags: -Ccodegen-units=2 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`
error: aborting due to previous error