diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index a78d692aaa7fb..26111729ba5b2 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -53,6 +53,9 @@ pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll V if enabled.contains(SanitizerSet::THREAD) { llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); } + if enabled.contains(SanitizerSet::HWADDRESS) { + llvm::Attribute::SanitizeHWAddress.apply_llfn(Function, llfn); + } } /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 326ae354ccf48..8b737c9a2e557 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -440,6 +440,8 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY), sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int, sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), + sanitize_hwaddress: config.sanitizer.contains(SanitizerSet::HWADDRESS), + sanitize_hwaddress_recover: config.sanitizer_recover.contains(SanitizerSet::HWADDRESS), }) } else { None @@ -652,6 +654,10 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static if config.sanitizer.contains(SanitizerSet::THREAD) { passes.push(llvm::LLVMRustCreateThreadSanitizerPass()); } + if config.sanitizer.contains(SanitizerSet::HWADDRESS) { + let recover = config.sanitizer_recover.contains(SanitizerSet::HWADDRESS); + passes.push(llvm::LLVMRustCreateHWAddressSanitizerPass(recover)); + } } pub(crate) fn link( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e82198f8f0c06..8c1740d8f25f0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -131,6 +131,7 @@ pub enum Attribute { ReturnsTwice = 25, ReadNone = 26, InaccessibleMemOnly = 27, + SanitizeHWAddress = 28, } /// LLVMIntPredicate @@ -439,6 +440,8 @@ pub struct SanitizerOptions { pub sanitize_memory_recover: bool, pub sanitize_memory_track_origins: c_int, pub sanitize_thread: bool, + pub sanitize_hwaddress: bool, + pub sanitize_hwaddress_recover: bool, } /// LLVMRelocMode @@ -2128,6 +2131,7 @@ extern "C" { Recover: bool, ) -> &'static mut Pass; pub fn LLVMRustCreateThreadSanitizerPass() -> &'static mut Pass; + pub fn LLVMRustCreateHWAddressSanitizerPass(Recover: bool) -> &'static mut Pass; pub fn LLVMRustAddPass(PM: &PassManager<'_>, Pass: &'static mut Pass); pub fn LLVMRustAddLastExtensionPasses( PMB: &PassManagerBuilder, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8bc4e64422370..6c58417590e69 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -893,6 +893,9 @@ fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linke if sanitizer.contains(SanitizerSet::THREAD) { link_sanitizer_runtime(sess, linker, "tsan"); } + if sanitizer.contains(SanitizerSet::HWADDRESS) { + link_sanitizer_runtime(sess, linker, "hwasan"); + } } fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 57b8664d3b605..0e3bf5615af7b 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -85,6 +85,7 @@ enum LLVMRustAttribute { ReturnsTwice = 25, ReadNone = 26, InaccessibleMemOnly = 27, + SanitizeHWAddress = 28, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 2264908995bb7..5263d5dcf3e8f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -33,6 +33,7 @@ #include "llvm/Support/TimeProfiler.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" +#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h" @@ -133,6 +134,12 @@ extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { return wrap(createThreadSanitizerLegacyPassPass()); } +extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) { + const bool CompileKernel = false; + + return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover)); +} + extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { assert(RustPass); Pass *Pass = unwrap(RustPass); @@ -722,6 +729,8 @@ struct LLVMRustSanitizerOptions { bool SanitizeMemoryRecover; int SanitizeMemoryTrackOrigins; bool SanitizeThread; + bool SanitizeHWAddress; + bool SanitizeHWAddressRecover; }; extern "C" void @@ -886,6 +895,23 @@ LLVMRustOptimizeWithNewPassManager( /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); } ); +#endif + } + if (SanitizerOptions->SanitizeHWAddress) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(HWAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover)); + } + ); +#else + PipelineStartEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM) { + MPM.addPass(HWAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover)); + } + ); #endif } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 4118e93074563..45835990cecbb 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -205,6 +205,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::ReadNone; case InaccessibleMemOnly: return Attribute::InaccessibleMemOnly; + case SanitizeHWAddress: + return Attribute::SanitizeHWAddress; } report_fatal_error("bad AttributeKind"); } diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 3ba06bdd6e05f..52b1ff3877da7 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -2191,19 +2191,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { CastKind::Pointer(PointerCast::ArrayToPointer) => { let ty_from = op.ty(body, tcx); - let opt_ty_elem = match ty_from.kind() { - ty::RawPtr(ty::TypeAndMut { - mutbl: hir::Mutability::Not, - ty: array_ty, - }) => match array_ty.kind() { - ty::Array(ty_elem, _) => Some(ty_elem), - _ => None, - }, + let opt_ty_elem_mut = match ty_from.kind() { + ty::RawPtr(ty::TypeAndMut { mutbl: array_mut, ty: array_ty }) => { + match array_ty.kind() { + ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)), + _ => None, + } + } _ => None, }; - let ty_elem = match opt_ty_elem { - Some(ty_elem) => ty_elem, + let (ty_elem, ty_mut) = match opt_ty_elem_mut { + Some(ty_elem_mut) => ty_elem_mut, None => { span_mirbug!( self, @@ -2215,11 +2214,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; - let ty_to = match ty.kind() { - ty::RawPtr(ty::TypeAndMut { - mutbl: hir::Mutability::Not, - ty: ty_to, - }) => ty_to, + let (ty_to, ty_to_mut) = match ty.kind() { + ty::RawPtr(ty::TypeAndMut { mutbl: ty_to_mut, ty: ty_to }) => { + (ty_to, *ty_to_mut) + } _ => { span_mirbug!( self, @@ -2231,6 +2229,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; + if ty_to_mut == Mutability::Mut && ty_mut == Mutability::Not { + span_mirbug!( + self, + rvalue, + "ArrayToPointer cast from const {:?} to mut {:?}", + ty, + ty_to + ); + return; + } + if let Err(terr) = self.sub_types( ty_elem, ty_to, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e9ea0ab6f98f5..210dbb0ee9939 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -43,6 +43,7 @@ bitflags! { const LEAK = 1 << 1; const MEMORY = 1 << 2; const THREAD = 1 << 3; + const HWADDRESS = 1 << 4; } } @@ -56,6 +57,7 @@ impl fmt::Display for SanitizerSet { SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", SanitizerSet::THREAD => "thread", + SanitizerSet::HWADDRESS => "hwaddress", _ => panic!("unrecognized sanitizer {:?}", s), }; if !first { @@ -73,12 +75,18 @@ impl IntoIterator for SanitizerSet { type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { - [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] - .iter() - .copied() - .filter(|&s| self.contains(s)) - .collect::>() - .into_iter() + [ + SanitizerSet::ADDRESS, + SanitizerSet::LEAK, + SanitizerSet::MEMORY, + SanitizerSet::THREAD, + SanitizerSet::HWADDRESS, + ] + .iter() + .copied() + .filter(|&s| self.contains(s)) + .collect::>() + .into_iter() } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f78df8a7e29fa..baa0502521da7 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -253,7 +253,7 @@ macro_rules! options { pub const parse_passes: &str = "a space-separated list of passes, or `all`"; pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `hwaddress`, `leak`, `memory` or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -476,6 +476,7 @@ macro_rules! options { "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, "thread" => SanitizerSet::THREAD, + "hwaddress" => SanitizerSet::HWADDRESS, _ => return false, } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 69aa72d899fb3..a7ceb9e06a519 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1126,7 +1126,8 @@ impl Session { self.opts.optimize != config::OptLevel::No // AddressSanitizer uses lifetimes to detect use after scope bugs. // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) + // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) } pub fn link_dead_code(&self) -> bool { @@ -1562,6 +1563,8 @@ fn validate_commandline_args_with_session_available(sess: &Session) { "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", ]; + const HWASAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-linux-android", "aarch64-unknown-linux-gnu"]; // Sanitizers can only be used on some tested platforms. for s in sess.opts.debugging_opts.sanitizer { @@ -1570,6 +1573,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS, SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS, SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS, + SanitizerSet::HWADDRESS => HWASAN_SUPPORTED_TARGETS, _ => panic!("unrecognized sanitizer {}", s), }; if !supported_targets.contains(&&*sess.opts.target_triple.triple()) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 20e4f7262acb9..1c37a6b2aca18 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -593,6 +593,7 @@ symbols! { html_no_source, html_playground_url, html_root_url, + hwaddress, i, i128, i128_type, diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 7924ffe8a6fd5..16c344e8e2b9e 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -765,9 +765,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { m_expr: ty::TypeAndMut<'tcx>, m_cast: ty::TypeAndMut<'tcx>, ) -> Result { - // array-ptr-cast. - - if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not { + // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const + if m_expr.mutbl == hir::Mutability::Mut || m_cast.mutbl == hir::Mutability::Not { if let ty::Array(ety, _) = m_expr.ty.kind() { // Due to the limitations of LLVM global constants, // region pointers end up pointing at copies of diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 04a9e65e6647d..75cad9f21c990 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -1211,7 +1211,7 @@ fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { ProjectionKind::Subslice => String::from("Subslice"), }; if i != 0 { - projections_str.push_str(","); + projections_str.push(','); } projections_str.push_str(proj.as_str()); } @@ -1382,14 +1382,8 @@ fn determine_place_ancestry_relation( // Assume of length of projections_b = m let projections_b = &place_b.projections; - let mut same_initial_projections = true; - - for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) { - if proj_a != proj_b { - same_initial_projections = false; - break; - } - } + let same_initial_projections = + projections_a.iter().zip(projections_b.iter()).all(|(proj_a, proj_b)| proj_a == proj_b); if same_initial_projections { // First min(n, m) projections are the same diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 66d158b0ee965..2598f3e38ce00 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -2709,10 +2709,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; } else if item.has_name(sym::thread) { codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; + } else if item.has_name(sym::hwaddress) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS; } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `memory` or `thread`") + .note("expected one of: `address`, `hwaddress`, `memory` or `thread`") .emit(); } } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index e4eabca9c3b76..7fa58dcd5f44f 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -29,6 +29,73 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let parent_node = tcx.hir().get(parent_node_id); match parent_node { + // This match arm is for when the def_id appears in a GAT whose + // path can't be resolved without typechecking e.g. + // + // trait Foo { + // type Assoc; + // fn foo() -> Self::Assoc<3>; + // } + // + // In the above code we would call this query with the def_id of 3 and + // the parent_node we match on would be the hir node for Self::Assoc<3> + // + // `Self::Assoc<3>` cant be resolved without typchecking here as we + // didnt write ::Assoc<3>. If we did then another match + // arm would handle this. + // + // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU + Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { + // Find the Item containing the associated type so we can create an ItemCtxt. + // Using the ItemCtxt convert the HIR for the unresolved assoc type into a + // ty which is a fully resolved projection. + // For the code example above, this would mean converting Self::Assoc<3> + // into a ty::Projection(::Assoc<3>) + let item_hir_id = tcx + .hir() + .parent_iter(hir_id) + .filter(|(_, node)| matches!(node, Node::Item(_))) + .map(|(id, _)| id) + .next() + .unwrap(); + let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); + let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; + let ty = item_ctxt.ast_ty_to_ty(hir_ty); + + // Iterate through the generics of the projection to find the one that corresponds to + // the def_id that this query was called with. We filter to only const args here as a + // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't + // but it can't hurt to be safe ^^ + if let ty::Projection(projection) = ty.kind() { + let generics = tcx.generics_of(projection.item_def_id); + + let arg_index = segment + .args + .and_then(|args| { + args.args + .iter() + .filter(|arg| arg.is_const()) + .position(|arg| arg.id() == hir_id) + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in segment"); + }); + + return generics + .params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) + .nth(arg_index) + .map(|param| param.def_id); + } + + // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + "unexpected non-GAT usage of an anon const", + ); + return None; + } Node::Expr(&Expr { kind: ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 542fa1a5acce0..22d95b8bcc08f 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -56,6 +56,7 @@ This API is completely unstable and subject to change. */ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(bindings_after_at)] #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 354efdefc2103..04033905728ab 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -99,13 +99,11 @@ //! pub struct Foo; //! //! #[no_mangle] -//! #[allow(improper_ctypes_definitions)] //! pub extern "C" fn foo_new() -> Box { //! Box::new(Foo) //! } //! //! #[no_mangle] -//! #[allow(improper_ctypes_definitions)] //! pub extern "C" fn foo_delete(_: Option>) {} //! ``` //! diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 98b8dca961407..9f98353452006 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -198,12 +198,13 @@ where Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i))) } } else if A::may_have_side_effect() && self.index < self.a.size() { + let i = self.index; + self.index += 1; // match the base implementation's potential side effects - // SAFETY: we just checked that `self.index` < `self.a.len()` + // SAFETY: we just checked that `i` < `self.a.len()` unsafe { - self.a.__iterator_get_unchecked(self.index); + self.a.__iterator_get_unchecked(i); } - self.index += 1; None } else { None diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 19a3b45e568c0..c99572d98ff82 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2082,6 +2082,12 @@ impl [T] { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search_by`]: #method.binary_search_by + /// [`binary_search_by_key`]: #method.binary_search_by_key + /// [`partition_point`]: #method.partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2129,6 +2135,12 @@ impl [T] { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search`]: #method.binary_search + /// [`binary_search_by_key`]: #method.binary_search_by_key + /// [`partition_point`]: #method.partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2186,7 +2198,12 @@ impl [T] { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`]. + /// /// [`sort_by_key`]: #method.sort_by_key + /// [`binary_search`]: #method.binary_search + /// [`binary_search_by`]: #method.binary_search_by + /// [`partition_point`]: #method.partition_point /// /// # Examples /// @@ -3399,11 +3416,15 @@ impl [T] { /// If this slice is not partitioned, the returned result is unspecified and meaningless, /// as this method performs a kind of binary search. /// + /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`]. + /// + /// [`binary_search`]: #method.binary_search + /// [`binary_search_by`]: #method.binary_search_by + /// [`binary_search_by_key`]: #method.binary_search_by_key + /// /// # Examples /// /// ``` - /// #![feature(partition_point)] - /// /// let v = [1, 2, 3, 3, 5, 6, 7]; /// let i = v.partition_point(|&x| x < 5); /// @@ -3411,7 +3432,7 @@ impl [T] { /// assert!(v[..i].iter().all(|&x| x < 5)); /// assert!(v[i..].iter().all(|&x| !(x < 5))); /// ``` - #[unstable(feature = "partition_point", reason = "new API", issue = "73831")] + #[stable(feature = "partition_point", since = "1.52.0")] pub fn partition_point

(&self, mut pred: P) -> usize where P: FnMut(&T) -> bool, diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 339691b117694..40dc6473b7d40 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -67,7 +67,6 @@ #![feature(option_result_unwrap_unchecked)] #![feature(option_unwrap_none)] #![feature(peekable_peek_mut)] -#![feature(partition_point)] #![feature(once_cell)] #![feature(unsafe_block_in_unsafe_fn)] #![feature(int_bits_const)] diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index d5e14bec76572..1d1118aa69434 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -22,6 +22,7 @@ use crate::str; use crate::sys::cvt; use crate::sys::fd; use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; +use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -490,20 +491,20 @@ pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; } - &mut environ + ptr::addr_of_mut!(environ) } -pub unsafe fn env_lock() -> StaticMutexGuard { - // It is UB to attempt to acquire this mutex reentrantly! - static ENV_LOCK: StaticMutex = StaticMutex::new(); - ENV_LOCK.lock() +static ENV_LOCK: StaticRWLock = StaticRWLock::new(); + +pub fn env_read_lock() -> RWLockReadGuard { + ENV_LOCK.read_with_guard() } /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { unsafe { - let _guard = env_lock(); + let _guard = env_read_lock(); let mut environ = *environ(); let mut result = Vec::new(); if !environ.is_null() { @@ -540,7 +541,7 @@ pub fn getenv(k: &OsStr) -> io::Result> { // always None as well let k = CString::new(k.as_bytes())?; unsafe { - let _guard = env_lock(); + let _guard = env_read_lock(); let s = libc::getenv(k.as_ptr()) as *const libc::c_char; let ret = if s.is_null() { None @@ -556,7 +557,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let v = CString::new(v.as_bytes())?; unsafe { - let _guard = env_lock(); + let _guard = ENV_LOCK.write_with_guard(); cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) } } @@ -565,7 +566,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; unsafe { - let _guard = env_lock(); + let _guard = ENV_LOCK.write_with_guard(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) } } diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 2746f87468dca..9e82df7755e89 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -47,7 +47,7 @@ impl Command { // a lock any more because the parent won't do anything and the child is // in its own process. let result = unsafe { - let _env_lock = sys::os::env_lock(); + let _env_lock = sys::os::env_read_lock(); cvt(libc::fork())? }; @@ -124,7 +124,7 @@ impl Command { // Similar to when forking, we want to ensure that access to // the environment is synchronized, so make sure to grab the // environment lock before we try to exec. - let _lock = sys::os::env_lock(); + let _lock = sys::os::env_read_lock(); let Err(e) = self.do_exec(theirs, envp.as_ref()); e @@ -404,7 +404,7 @@ impl Command { cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource - let _env_lock = sys::os::env_lock(); + let _env_lock = sys::os::env_read_lock(); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); cvt_nz(libc::posix_spawnp( &mut p.pid, diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 3705d641a1be6..41e8ad7729463 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -86,3 +86,62 @@ impl RWLock { self.0.destroy() } } + +// the cfg annotations only exist due to dead code warnings. the code itself is portable +#[cfg(unix)] +pub struct StaticRWLock(RWLock); + +#[cfg(unix)] +impl StaticRWLock { + pub const fn new() -> StaticRWLock { + StaticRWLock(RWLock::new()) + } + + /// Acquires shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn read_with_guard(&'static self) -> RWLockReadGuard { + // Safety: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.read(); + } + RWLockReadGuard(&self.0) + } + + /// Acquires write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn write_with_guard(&'static self) -> RWLockWriteGuard { + // Safety: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.write(); + } + RWLockWriteGuard(&self.0) + } +} + +#[cfg(unix)] +pub struct RWLockReadGuard(&'static RWLock); + +#[cfg(unix)] +impl Drop for RWLockReadGuard { + fn drop(&mut self) { + unsafe { self.0.read_unlock() } + } +} + +#[cfg(unix)] +pub struct RWLockWriteGuard(&'static RWLock); + +#[cfg(unix)] +impl Drop for RWLockWriteGuard { + fn drop(&mut self) { + unsafe { self.0.write_unlock() } + } +} diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 2cabaee68ea67..2e6e9142afe6c 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -51,7 +51,7 @@ def v(*args): o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)") o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date") o("vendor", "build.vendor", "enable usage of vendored Rust crates") -o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan)") +o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan, hwasan)") o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball") o("cargo-native-static", "build.cargo-native-static", "static native libraries in cargo") o("profiler", "build.profiler", "build the profiler runtime") diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 609ac8b366952..b5a8b694c9420 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -804,7 +804,7 @@ fn supported_sanitizers( "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { - common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan"]) + common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) } "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index d03d5c7501424..4f7a101d2acbd 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -7,12 +7,15 @@ The tracking issue for this feature is: [#39699](https://github.com/rust-lang/ru This feature allows for use of one of following sanitizers: * [AddressSanitizer][clang-asan] a fast memory error detector. +* [HWAddressSanitizer][clang-hwasan] a memory error detector similar to + AddressSanitizer, but based on partial hardware assistance. * [LeakSanitizer][clang-lsan] a run-time memory leak detector. * [MemorySanitizer][clang-msan] a detector of uninitialized reads. * [ThreadSanitizer][clang-tsan] a fast data race detector. -To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`, -`-Zsanitizer=memory` or `-Zsanitizer=thread`. +To enable a sanitizer compile with `-Zsanitizer=address`, +`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or +`-Zsanitizer=thread`. # AddressSanitizer @@ -174,6 +177,86 @@ Shadow byte legend (one shadow byte represents 8 application bytes): ==39249==ABORTING ``` +# HWAddressSanitizer + +HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much +less memory. + +HWAddressSanitizer is supported on the following targets: + +* `aarch64-linux-android` +* `aarch64-unknown-linux-gnu` + +HWAddressSanitizer requires `tagged-globals` target feature to instrument +globals. To enable this target feature compile with `-C +target-feature=+tagged-globals` + +## Example + +Heap buffer overflow: + +```rust +fn main() { + let xs = vec![0, 1, 2, 3]; + let _y = unsafe { *xs.as_ptr().offset(4) }; +} +``` + +```shell +$ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C +linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target +aarch64-unknown-linux-gnu +``` + +```shell +$ ./main +==241==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffff0050 at pc 0xaaaae0ae4a98 +READ of size 4 at 0xefdeffff0050 tags: 2c/00 (ptr/mem) in thread T0 + #0 0xaaaae0ae4a94 (/.../main+0x54a94) + ... + +[0xefdeffff0040,0xefdeffff0060) is a small allocated heap chunk; size: 32 offset: 16 +0xefdeffff0050 is located 0 bytes to the right of 16-byte region [0xefdeffff0040,0xefdeffff0050) +allocated here: + #0 0xaaaae0acb80c (/.../main+0x3b80c) + ... + +Thread: T0 0xeffe00002000 stack: [0xffffc28ad000,0xffffc30ad000) sz: 8388608 tls: [0xffffaa10a020,0xffffaa10a7d0) +Memory tags around the buggy address (one tag corresponds to 16 bytes): + 0xfefcefffef80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffef90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffefe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefcefffeff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0xfefceffff000: d7 d7 05 00 2c [00] 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0xfefceffff080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Tags for short granules around the buggy address (one tag corresponds to 16 bytes): + 0xfefcefffeff0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. +=>0xfefceffff000: .. .. 8c .. .. [..] .. .. .. .. .. .. .. .. .. .. + 0xfefceffff010: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. +See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags +Registers where the failure occurred (pc 0xaaaae0ae4a98): + x0 2c00efdeffff0050 x1 0000000000000004 x2 0000000000000004 x3 0000000000000000 + x4 0000fffefc30ac37 x5 000000000000005d x6 00000ffffc30ac37 x7 0000efff00000000 + x8 2c00efdeffff0050 x9 0200efff00000000 x10 0000000000000000 x11 0200efff00000000 + x12 0200effe00000310 x13 0200effe00000310 x14 0000000000000008 x15 5d00ffffc30ac360 + x16 0000aaaae0ad062c x17 0000000000000003 x18 0000000000000001 x19 0000ffffc30ac658 + x20 4e00ffffc30ac6e0 x21 0000aaaae0ac5e10 x22 0000000000000000 x23 0000000000000000 + x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000 + x28 0000000000000000 x29 0000ffffc30ac5a0 x30 0000aaaae0ae4a98 +SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94) +``` + # LeakSanitizer LeakSanitizer is run-time memory leak detector. @@ -321,11 +404,13 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT * [Sanitizers project page](https://github.com/google/sanitizers/wiki/) * [AddressSanitizer in Clang][clang-asan] +* [HWAddressSanitizer in Clang][clang-hwasan] * [LeakSanitizer in Clang][clang-lsan] * [MemorySanitizer in Clang][clang-msan] * [ThreadSanitizer in Clang][clang-tsan] [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html +[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index eecfd337cdf84..fb4774ae19246 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -546,9 +546,12 @@ crate fn make_test( // compiler. if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") { if let Some(cratename) = cratename { - // Make sure its actually used if not included. + // Don't inject `extern crate` if the crate is never used. + // NOTE: this is terribly inaccurate because it doesn't actually + // parse the source, but only has false positives, not false + // negatives. if s.contains(cratename) { - prog.push_str(&format!("extern crate {};\n", cratename)); + prog.push_str(&format!("extern crate r#{};\n", cratename)); line_offset += 1; } } diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 465b2b1d69b34..c49e45c0e25a0 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -38,7 +38,7 @@ fn make_test_crate_name() { let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] -extern crate asdf; +extern crate r#asdf; fn main() { use asdf::qwop; assert_eq!(2+2, 4); @@ -128,7 +128,7 @@ fn make_test_opts_attrs() { let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![feature(sick_rad)] -extern crate asdf; +extern crate r#asdf; fn main() { use asdf::qwop; assert_eq!(2+2, 4); @@ -141,7 +141,7 @@ assert_eq!(2+2, 4); opts.attrs.push("feature(hella_dope)".to_string()); let expected = "#![feature(sick_rad)] #![feature(hella_dope)] -extern crate asdf; +extern crate r#asdf; fn main() { use asdf::qwop; assert_eq!(2+2, 4); @@ -250,7 +250,7 @@ assert_eq!(asdf::foo, 4);"; let expected = "#![allow(unused)] extern crate hella_qwop; -extern crate asdf; +extern crate r#asdf; fn main() { assert_eq!(asdf::foo, 4); }" diff --git a/src/test/rustdoc/playground-arg.rs b/src/test/rustdoc/playground-arg.rs index 018716ad45af3..dbe2297f81887 100644 --- a/src/test/rustdoc/playground-arg.rs +++ b/src/test/rustdoc/playground-arg.rs @@ -11,4 +11,4 @@ pub fn dummy() {} // ensure that `extern crate foo;` was inserted into code snips automatically: -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20r%23foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run" diff --git a/src/test/ui/array-slice-vec/vector-cast-weirdness.rs b/src/test/ui/array-slice-vec/vector-cast-weirdness.rs index 79b9243765b95..e8f2c71477a5f 100644 --- a/src/test/ui/array-slice-vec/vector-cast-weirdness.rs +++ b/src/test/ui/array-slice-vec/vector-cast-weirdness.rs @@ -1,7 +1,11 @@ // Issue #14893. Tests that casts from vectors don't behave strangely in the // presence of the `_` type shorthand notation. +// // Update: after a change to the way casts are done, we have more type information // around and so the errors here are no longer exactly the same. +// +// Update: With PR #81479 some of the previously rejected cases are now allowed. +// New test cases added. struct X { y: [u8; 2], @@ -12,13 +16,19 @@ fn main() { // No longer a type mismatch - the `_` can be fully resolved by type inference. let p1: *const u8 = &x1.y as *const _; + let p1: *mut u8 = &x1.y as *mut _; + //~^ ERROR: casting `&[u8; 2]` as `*mut u8` is invalid let t1: *const [u8; 2] = &x1.y as *const _; + let t1: *mut [u8; 2] = &x1.y as *mut _; + //~^ ERROR: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid let h1: *const [u8; 2] = &x1.y as *const [u8; 2]; + let t1: *mut [u8; 2] = &x1.y as *mut [u8; 2]; + //~^ ERROR: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid let mut x1 = X { y: [0, 0] }; - // This is still an error since we don't allow casts from &mut [T; n] to *mut T. - let p1: *mut u8 = &mut x1.y as *mut _; //~ ERROR casting + let p1: *mut u8 = &mut x1.y as *mut _; + let p2: *const u8 = &mut x1.y as *const _; let t1: *mut [u8; 2] = &mut x1.y as *mut _; let h1: *mut [u8; 2] = &mut x1.y as *mut [u8; 2]; } diff --git a/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr b/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr index 37055bb75f559..6fdb1ac9e3059 100644 --- a/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr +++ b/src/test/ui/array-slice-vec/vector-cast-weirdness.stderr @@ -1,9 +1,21 @@ -error[E0606]: casting `&mut [u8; 2]` as `*mut u8` is invalid - --> $DIR/vector-cast-weirdness.rs:21:23 +error[E0606]: casting `&[u8; 2]` as `*mut u8` is invalid + --> $DIR/vector-cast-weirdness.rs:19:23 | -LL | let p1: *mut u8 = &mut x1.y as *mut _; - | ^^^^^^^^^^^^^^^^^^^ +LL | let p1: *mut u8 = &x1.y as *mut _; + | ^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0606]: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid + --> $DIR/vector-cast-weirdness.rs:22:28 + | +LL | let t1: *mut [u8; 2] = &x1.y as *mut _; + | ^^^^^^^^^^^^^^^ + +error[E0606]: casting `&[u8; 2]` as `*mut [u8; 2]` is invalid + --> $DIR/vector-cast-weirdness.rs:25:28 + | +LL | let t1: *mut [u8; 2] = &x1.y as *mut [u8; 2]; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0606`. diff --git a/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-1.rs b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-1.rs new file mode 100644 index 0000000000000..ab33ef6f2442c --- /dev/null +++ b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-1.rs @@ -0,0 +1,22 @@ +// run-pass +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +// This test unsures that with_opt_const_param returns the +// def_id of the N param in the Foo::Assoc GAT. + +trait Foo { + type Assoc; + fn foo(&self) -> Self::Assoc<3>; +} + +impl Foo for () { + type Assoc = [(); N]; + fn foo(&self) -> Self::Assoc<3> { + [(); 3] + } +} + +fn main() { + assert_eq!(().foo(), [(); 3]); +} diff --git a/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-2.rs b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-2.rs new file mode 100644 index 0000000000000..ba9a82ae72109 --- /dev/null +++ b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-2.rs @@ -0,0 +1,22 @@ +// run-pass +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +// This test unsures that with_opt_const_param returns the +// def_id of the N param in the Foo::Assoc GAT. + +trait Foo { + type Assoc; + fn foo(&self) -> Self::Assoc; +} + +impl Foo for () { + type Assoc = [(); N]; + fn foo(&self) -> Self::Assoc { + [(); N] + } +} + +fn main() { + assert_eq!(().foo::<10>(), [(); 10]); +} diff --git a/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-3.rs b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-3.rs new file mode 100644 index 0000000000000..9da5334056a37 --- /dev/null +++ b/src/test/ui/generic-associated-types/const-generics-gat-in-trait-return-type-3.rs @@ -0,0 +1,27 @@ +// run-pass +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +// This test unsures that with_opt_const_param returns the +// def_id of the N param in the Bar::Assoc GAT. + +trait Bar { + type Assoc; +} +trait Foo: Bar { + fn foo(&self) -> Self::Assoc<3>; +} + +impl Bar for () { + type Assoc = [(); N]; +} + +impl Foo for () { + fn foo(&self) -> Self::Assoc<3> { + [(); 3] + } +} + +fn main() { + assert_eq!(().foo(), [(); 3]); +} diff --git a/src/test/ui/invalid/invalid-no-sanitize.stderr b/src/test/ui/invalid/invalid-no-sanitize.stderr index e9983e5fbd24d..4c0b17c7d3769 100644 --- a/src/test/ui/invalid/invalid-no-sanitize.stderr +++ b/src/test/ui/invalid/invalid-no-sanitize.stderr @@ -4,7 +4,7 @@ error: invalid argument for `no_sanitize` LL | #[no_sanitize(brontosaurus)] | ^^^^^^^^^^^^ | - = note: expected one of: `address`, `memory` or `thread` + = note: expected one of: `address`, `hwaddress`, `memory` or `thread` error: aborting due to previous error diff --git a/src/test/ui/sanitize/hwaddress.rs b/src/test/ui/sanitize/hwaddress.rs new file mode 100644 index 0000000000000..ad5d0245457ec --- /dev/null +++ b/src/test/ui/sanitize/hwaddress.rs @@ -0,0 +1,19 @@ +// needs-sanitizer-support +// needs-sanitizer-hwaddress +// +// compile-flags: -Z sanitizer=hwaddress -O -g +// +// run-fail +// error-pattern: HWAddressSanitizer: tag-mismatch + +#![feature(test)] + +use std::hint::black_box; + +fn main() { + let xs = vec![0, 1, 2, 3]; + // Avoid optimizing everything out. + let xs = black_box(xs.as_ptr()); + let code = unsafe { *xs.offset(4) }; + std::process::exit(code); +} diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 429a8c98cd57c..ff0d845be937c 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -48,6 +48,7 @@ impl EarlyProps { let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); iter_header(testfile, None, rdr, &mut |ln| { // we should check if any only- exists and if it exists @@ -101,6 +102,10 @@ impl EarlyProps { props.ignore = true; } + if !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress") { + props.ignore = true; + } + if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) { props.ignore = true; } diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 292850bd9e277..b302953708c18 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -110,6 +110,9 @@ pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[ "x86_64-unknown-linux-gnu", ]; +pub const HWASAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-linux-android", "aarch64-unknown-linux-gnu"]; + const BIG_ENDIAN: &[&str] = &[ "aarch64_be", "armebv7r",