Auto merge of #82045 - Dylan-DPC:rollup-244l0sb, r=Dylan-DPC

Rollup of 10 pull requests

Successful merges:

 - #79775 (Fix injected errors when running doctests on a crate named after a keyword)
 - #81012 (Stabilize the partition_point feature)
 - #81479 (Allow casting mut array ref to mut ptr)
 - #81506 (HWAddressSanitizer support)
 - #81741 (Increment `self.index` before calling `Iterator::self.a.__iterator_ge…)
 - #81850 (use RWlock when accessing os::env)
 - #81911 (GAT/const_generics: Allow with_opt_const_param to return GAT param def_id)
 - #82022 (Push a `char` instead of a `str` with len one into a String)
 - #82023 (Remove unnecessary lint allow attrs on example)
 - #82030 (Use `Iterator::all` instead of open-coding it)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2021-02-13 02:46:57 +00:00
commit 21cbbdc44d
39 changed files with 496 additions and 78 deletions

View file

@ -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.

View file

@ -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(

View file

@ -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,

View file

@ -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) {

View file

@ -85,6 +85,7 @@ enum LLVMRustAttribute {
ReturnsTwice = 25,
ReadNone = 26,
InaccessibleMemOnly = 27,
SanitizeHWAddress = 28,
};
typedef struct OpaqueRustString *RustStringRef;

View file

@ -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
}
}

View file

@ -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");
}

View file

@ -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,

View file

@ -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<SanitizerSet>;
fn into_iter(self) -> Self::IntoIter {
[SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD]
.iter()
.copied()
.filter(|&s| self.contains(s))
.collect::<Vec<_>>()
.into_iter()
[
SanitizerSet::ADDRESS,
SanitizerSet::LEAK,
SanitizerSet::MEMORY,
SanitizerSet::THREAD,
SanitizerSet::HWADDRESS,
]
.iter()
.copied()
.filter(|&s| self.contains(s))
.collect::<Vec<_>>()
.into_iter()
}
}

View file

@ -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,
}
}

View file

@ -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()) {

View file

@ -593,6 +593,7 @@ symbols! {
html_no_source,
html_playground_url,
html_root_url,
hwaddress,
i,
i128,
i128_type,

View file

@ -765,9 +765,8 @@ impl<'a, 'tcx> CastCheck<'tcx> {
m_expr: ty::TypeAndMut<'tcx>,
m_cast: ty::TypeAndMut<'tcx>,
) -> Result<CastKind, CastError> {
// 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

View file

@ -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

View file

@ -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();
}
}

View file

@ -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<const N: usize>;
// 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 <Self as Foo>::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(<Self as Foo>::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)),

View file

@ -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)]

View file

@ -99,13 +99,11 @@
//! pub struct Foo;
//!
//! #[no_mangle]
//! #[allow(improper_ctypes_definitions)]
//! pub extern "C" fn foo_new() -> Box<Foo> {
//! Box::new(Foo)
//! }
//!
//! #[no_mangle]
//! #[allow(improper_ctypes_definitions)]
//! pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}
//! ```
//!

View file

@ -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() {
// match the base implementation's potential side effects
// SAFETY: we just checked that `self.index` < `self.a.len()`
unsafe {
self.a.__iterator_get_unchecked(self.index);
}
let i = self.index;
self.index += 1;
// match the base implementation's potential side effects
// SAFETY: we just checked that `i` < `self.a.len()`
unsafe {
self.a.__iterator_get_unchecked(i);
}
None
} else {
None

View file

@ -2082,6 +2082,12 @@ impl<T> [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> [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> [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> [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> [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<P>(&self, mut pred: P) -> usize
where
P: FnMut(&T) -> bool,

View file

@ -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)]

View file

@ -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<Option<OsString>> {
// 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)
}
}

View file

@ -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,

View file

@ -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() }
}
}

View file

@ -51,7 +51,7 @@ o("option-checking", None, "complain about unrecognized options in this configur
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")

View file

@ -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"]),

View file

@ -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

View file

@ -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;
}
}

View file

@ -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);
}"

View file

@ -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"

View file

@ -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];
}

View file

@ -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`.

View file

@ -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<const N: usize>;
fn foo(&self) -> Self::Assoc<3>;
}
impl Foo for () {
type Assoc<const N: usize> = [(); N];
fn foo(&self) -> Self::Assoc<3> {
[(); 3]
}
}
fn main() {
assert_eq!(().foo(), [(); 3]);
}

View file

@ -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<const N: usize>;
fn foo<const N: usize>(&self) -> Self::Assoc<N>;
}
impl Foo for () {
type Assoc<const N: usize> = [(); N];
fn foo<const N: usize>(&self) -> Self::Assoc<N> {
[(); N]
}
}
fn main() {
assert_eq!(().foo::<10>(), [(); 10]);
}

View file

@ -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<const N: usize>;
}
trait Foo: Bar {
fn foo(&self) -> Self::Assoc<3>;
}
impl Bar for () {
type Assoc<const N: usize> = [(); N];
}
impl Foo for () {
fn foo(&self) -> Self::Assoc<3> {
[(); 3]
}
}
fn main() {
assert_eq!(().foo(), [(); 3]);
}

View file

@ -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

View file

@ -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);
}

View file

@ -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-<platform> 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;
}

View file

@ -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",