Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
llfn,
&cx.tcx.codegen_instance_attrs(instance.def),
Some(instance),
cx.sanitizer_ignorelist.as_ref(),
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ fn create_wrapper_function(
ty,
);

llfn_attrs_from_instance(cx, tcx, llfn, attrs, None);
llfn_attrs_from_instance(cx, tcx, llfn, attrs, None, None);

let no_return = if no_return {
// -> ! DIFlagNoReturn
Expand Down
65 changes: 63 additions & 2 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>(
cx: &SimpleCx<'ll>,
tcx: TyCtxt<'tcx>,
sanitizer_fn_attr: SanitizerFnAttrs,
enabled: SanitizerSet,
) -> SmallVec<[&'ll Attribute; 4]> {
let mut attrs = SmallVec::new();
let enabled = tcx.sess.sanitizers() - sanitizer_fn_attr.disabled;
if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
}
Expand Down Expand Up @@ -409,6 +409,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
llfn: &'ll Value,
codegen_fn_attrs: &CodegenFnAttrs,
instance: Option<ty::Instance<'tcx>>,
sanitizer_ignorelist: Option<&crate::llvm::SanitizerIgnoreList>,
) {
let sess = tcx.sess;
let mut to_add = SmallVec::<[_; 16]>::new();
Expand Down Expand Up @@ -470,7 +471,67 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
// not used.
} else {
// Do not set sanitizer attributes for naked functions.
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
let mut enabled = tcx.sess.sanitizers() - codegen_fn_attrs.sanitizers.disabled;
if let Some(ignorelist) = sanitizer_ignorelist {
if let Some(instance) = instance {
let sym_name = tcx.symbol_name(instance).name;
let span = tcx.def_span(instance.def_id());
let source_map = tcx.sess.source_map();
let filename =
source_map.span_to_filename(span).prefer_local_unconditionally().to_string();

let mainfile = tcx
.sess
.local_crate_source_file()
.and_then(|path| path.local_path().map(|p| p.display().to_string()))
.unwrap_or_default();

let demangled = rustc_middle::ty::print::with_no_trimmed_paths!(
tcx.def_path_str(instance.def_id())
);
let is_ignored = |section: &std::ffi::CStr| -> bool {
ignorelist.contains_prefix(section, c"fun", sym_name)
|| ignorelist.contains_prefix(section, c"fun", &demangled)
|| ignorelist.contains_prefix(section, c"src", &filename)
|| (!mainfile.is_empty()
&& ignorelist.contains_prefix(section, c"mainfile", &mainfile))
};

let ignore_address = is_ignored(c"address");
let ignore_kernel_address = ignore_address || is_ignored(c"kernel-address");
let ignore_hwaddress = is_ignored(c"hwaddress");
let ignore_kernel_hwaddress = ignore_hwaddress || is_ignored(c"kernel-hwaddress");

if enabled.contains(SanitizerSet::ADDRESS) && ignore_address {
enabled.remove(SanitizerSet::ADDRESS);
}
if enabled.contains(SanitizerSet::KERNELADDRESS) && ignore_kernel_address {
enabled.remove(SanitizerSet::KERNELADDRESS);
}
if enabled.contains(SanitizerSet::MEMORY) && is_ignored(c"memory") {
enabled.remove(SanitizerSet::MEMORY);
}
if enabled.contains(SanitizerSet::THREAD) && is_ignored(c"thread") {
enabled.remove(SanitizerSet::THREAD);
}
if enabled.contains(SanitizerSet::HWADDRESS) && ignore_hwaddress {
enabled.remove(SanitizerSet::HWADDRESS);
}
if enabled.contains(SanitizerSet::KERNELHWADDRESS) && ignore_kernel_hwaddress {
enabled.remove(SanitizerSet::KERNELHWADDRESS);
}
if enabled.contains(SanitizerSet::SAFESTACK) && is_ignored(c"safestack") {
enabled.remove(SanitizerSet::SAFESTACK);
}
if is_ignored(c"cfi") {
to_add.push(llvm::CreateAttrString(cx.llcx, "no-sanitize-cfi"));
}
if is_ignored(c"kcfi") {
to_add.push(llvm::CreateAttrString(cx.llcx, "no-sanitize-kcfi"));
}
}
}
to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers, enabled));

// For non-naked functions, set branch protection attributes on aarch64.
if let Some(BranchProtection { bti, pac_ret, gcs }) =
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_codegen_llvm/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ pub(crate) fn compile_codegen_unit(
if let Some(entry) =
maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx, cx.codegen_unit)
{
let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default());
let attrs = attributes::sanitize_attrs(
&cx,
tcx,
SanitizerFnAttrs::default(),
tcx.sess.sanitizers(),
);
attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs);
}

Expand Down
88 changes: 64 additions & 24 deletions compiler/rustc_codegen_llvm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1832,7 +1832,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {

// Emit KCFI operand bundle
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn);
if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) {
if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|bundle| bundle.as_ref()) {
bundles.push(kcfi_bundle);
}

Expand Down Expand Up @@ -1875,6 +1875,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
{
return;
}
if crate::llvm::HasStringAttribute(self.llfn(), "no-sanitize-cfi") {
return;
}

let mut options = cfi::TypeIdOptions::empty();
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
Expand All @@ -1884,11 +1887,29 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS);
}

let inputs: Vec<_> = fn_abi.args.iter().map(|arg| arg.layout.ty).collect();
let output = fn_abi.ret.layout.ty;
let mut fn_sig_kind = rustc_middle::ty::FnSigKind::default();
fn_sig_kind = fn_sig_kind.set_safety(rustc_hir::Safety::Safe);
fn_sig_kind = fn_sig_kind.set_c_variadic(fn_abi.c_variadic);
let fn_sig = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind);
let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig));
let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string());
Comment thread
1c3t3a marked this conversation as resolved.
Outdated

let typeid = if let Some(instance) = instance {
cfi::typeid_for_instance(self.tcx, instance, options)
} else {
cfi::typeid_for_fnabi(self.tcx, fn_abi, options)
};

if self
.cx
.sanitizer_ignorelist
.as_ref()
.is_some_and(|ignorelist| ignorelist.contains_prefix(c"cfi", c"type", &type_name))
{
return;
}
Comment thread
1c3t3a marked this conversation as resolved.
Outdated
let typeid_metadata = self.cx.create_metadata(typeid.as_bytes());
let dbg_loc = self.get_dbg_loc();

Expand Down Expand Up @@ -1924,34 +1945,53 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
llfn: &'ll Value,
) -> Option<llvm::OperandBundleBox<'ll>> {
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.sanitizers.disabled.contains(SanitizerSet::KCFI)
let kcfi_bundle =
if self.tcx.sess.is_sanitizer_kcfi_enabled()
&& let Some(fn_abi) = fn_abi
&& is_indirect_call
{
return None;
}
if let Some(fn_attrs) = fn_attrs
&& fn_attrs.sanitizers.disabled.contains(SanitizerSet::KCFI)
{
return None;
}
if crate::llvm::HasStringAttribute(self.llfn(), "no-sanitize-kcfi") {
return None;
}

let mut options = kcfi::TypeIdOptions::empty();
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS);
}
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS);
}
let mut options = kcfi::TypeIdOptions::empty();
if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS);
}
if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS);
}

let inputs: Vec<_> = fn_abi.args.iter().map(|arg| arg.layout.ty).collect();
let output = fn_abi.ret.layout.ty;
let mut fn_sig_kind = rustc_middle::ty::FnSigKind::default();
fn_sig_kind = fn_sig_kind.set_safety(rustc_hir::Safety::Safe);
fn_sig_kind = fn_sig_kind.set_c_variadic(fn_abi.c_variadic);
let fn_sig = self.tcx.mk_fn_sig(inputs, output, fn_sig_kind);
let fn_ptr = Ty::new_fn_ptr(self.tcx, rustc_middle::ty::Binder::dummy(fn_sig));
let type_name = rustc_middle::ty::print::with_no_trimmed_paths!(fn_ptr.to_string());

if self.cx.sanitizer_ignorelist.as_ref().is_some_and(|ignorelist| {
ignorelist.contains_prefix(c"kcfi", c"type", &type_name)
}) {
return None;
}
Comment thread
1c3t3a marked this conversation as resolved.
Outdated

let kcfi_typeid = if let Some(instance) = instance {
kcfi::typeid_for_instance(self.tcx, instance, options)
} else {
kcfi::typeid_for_fnabi(self.tcx, fn_abi, options)
};

let kcfi_typeid = if let Some(instance) = instance {
kcfi::typeid_for_instance(self.tcx, instance, options)
Some(llvm::OperandBundleBox::new("kcfi", &[self.const_u32(kcfi_typeid)]))
} else {
kcfi::typeid_for_fnabi(self.tcx, fn_abi, options)
None
};

Some(llvm::OperandBundleBox::new("kcfi", &[self.const_u32(kcfi_typeid)]))
} else {
None
};
kcfi_bundle
}

Expand Down
46 changes: 46 additions & 0 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,52 @@ impl<'ll> CodegenCx<'ll, '_> {

base::set_variable_sanitizer_attrs(g, attrs);

if let Some(ignorelist) = &self.sanitizer_ignorelist {
let instance = ty::Instance::mono(self.tcx, def_id);
let sym_name = self.tcx.symbol_name(instance).name;
let span = self.tcx.def_span(def_id);
let source_map = self.tcx.sess.source_map();
let filename =
source_map.span_to_filename(span).prefer_local_unconditionally().to_string();
let ty_name = rustc_middle::ty::print::with_no_trimmed_paths!(
self.tcx.type_of(def_id).skip_binder().to_string()
);
let mainfile = self
.tcx
.sess
.local_crate_source_file()
.and_then(|path| path.local_path().map(|p| p.display().to_string()))
.unwrap_or_default();

let is_ignored = |section: &std::ffi::CStr| -> bool {
ignorelist.contains_prefix(section, c"global", sym_name)
|| ignorelist.contains_prefix(section, c"src", &filename)
|| (!mainfile.is_empty()
&& ignorelist.contains_prefix(section, c"mainfile", &mainfile))
|| ignorelist.contains_prefix(section, c"type", &ty_name)
};

let sanitizers = self.tcx.sess.sanitizers();
let ignore_address = is_ignored(c"address");
let ignore_kernel_address = ignore_address || is_ignored(c"kernel-address");
let ignore_hwaddress = is_ignored(c"hwaddress");
let ignore_kernel_hwaddress = ignore_hwaddress || is_ignored(c"kernel-hwaddress");

if (sanitizers.contains(rustc_target::spec::SanitizerSet::ADDRESS) && ignore_address)
|| (sanitizers.contains(rustc_target::spec::SanitizerSet::KERNELADDRESS)
&& ignore_kernel_address)
{
unsafe { llvm::LLVMRustSetNoSanitizeAddress(g) };
}
if (sanitizers.contains(rustc_target::spec::SanitizerSet::HWADDRESS)
&& ignore_hwaddress)
|| (sanitizers.contains(rustc_target::spec::SanitizerSet::KERNELHWADDRESS)
&& ignore_kernel_hwaddress)
{
unsafe { llvm::LLVMRustSetNoSanitizeHWAddress(g) };
}
}

if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) {
// `USED` and `USED_LINKER` can't be used together.
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER));
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ pub(crate) struct FullCx<'ll, 'tcx> {
/// Extra per-CGU codegen state needed when coverage instrumentation is enabled.
pub coverage_cx: Option<coverageinfo::CguCoverageContext<'ll, 'tcx>>,
pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,
pub sanitizer_ignorelist: Option<crate::llvm::SanitizerIgnoreList>,

eh_personality: Cell<Option<&'ll Value>>,
pub rust_try_fn: Cell<Option<(&'ll Type, &'ll Value)>>,
Expand Down Expand Up @@ -650,6 +651,26 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
None
};

// FIXME: This parses the ignorelist files for each CGU, which adds a performance overhead.
// Clang parses it once per frontend invocation. LLVM's `SpecialCaseList::inSection`
// mutates an internal `LazyInit` cache and is not thread-safe. We either need to wrap
// the queries in a lock or wait for LLVM to expose a thread-safe way to query it.
let sanitizer_ignorelist = if !tcx.sess.opts.unstable_opts.sanitizer_ignorelist.is_empty() {
for path in &tcx.sess.opts.unstable_opts.sanitizer_ignorelist {
let _ = tcx.sess.source_map().load_file(std::path::Path::new(path));
}
match crate::llvm::SanitizerIgnoreList::new(
&tcx.sess.opts.unstable_opts.sanitizer_ignorelist,
) {
Ok(list) => Some(list),
Err(err) => {
tcx.dcx().fatal(format!("failed to parse sanitizer ignorelist: {}", err));
}
}
} else {
None
};

GenericCx(
FullCx {
tcx,
Expand All @@ -669,6 +690,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
scalar_lltypes: Default::default(),
coverage_cx,
dbg_cx,
sanitizer_ignorelist,
eh_personality: Cell::new(None),
rust_try_fn: Cell::new(None),
intrinsics: Default::default(),
Expand Down
Loading
Loading