| use std::collections::{BTreeMap, BTreeSet}; |
| use std::str::FromStr; |
| |
| use rustc_macros::{BlobDecodable, Encodable}; |
| use rustc_span::edition::Edition; |
| use rustc_target::spec::StackProtector; |
| |
| use crate::Session; |
| use crate::config::Options; |
| use crate::options::CFGuard; |
| |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Encodable, BlobDecodable)] |
| pub enum DeniedPartialMitigationLevel { |
| // Enabled(false) should be the bottom of the Ord hierarchy |
| Enabled(bool), |
| StackProtector(StackProtector), |
| } |
| |
| impl DeniedPartialMitigationLevel { |
| pub fn level_str(&self) -> &'static str { |
| match self { |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::All) => "=all", |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::Basic) => "=basic", |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::Strong) => "=strong", |
| // currently `=disabled` should not appear |
| DeniedPartialMitigationLevel::Enabled(false) => "=disabled", |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::None) |
| | DeniedPartialMitigationLevel::Enabled(true) => "", |
| } |
| } |
| } |
| |
| impl std::fmt::Display for DeniedPartialMitigationLevel { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| match self { |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::All) => { |
| write!(f, "all") |
| } |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::Basic) => { |
| write!(f, "basic") |
| } |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::Strong) => { |
| write!(f, "strong") |
| } |
| DeniedPartialMitigationLevel::Enabled(true) => { |
| write!(f, "enabled") |
| } |
| DeniedPartialMitigationLevel::StackProtector(StackProtector::None) |
| | DeniedPartialMitigationLevel::Enabled(false) => { |
| write!(f, "disabled") |
| } |
| } |
| } |
| } |
| |
| impl From<bool> for DeniedPartialMitigationLevel { |
| fn from(value: bool) -> Self { |
| DeniedPartialMitigationLevel::Enabled(value) |
| } |
| } |
| |
| impl From<StackProtector> for DeniedPartialMitigationLevel { |
| fn from(value: StackProtector) -> Self { |
| DeniedPartialMitigationLevel::StackProtector(value) |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| struct MitigationStatus { |
| // This is the index of the option in the command line. This is needed because |
| // re-enabling a mitigation resets the partial mitigation status if it's later in the command |
| // line, and this works across `-C` and `-Z` args. |
| // |
| // e.g. `-Z stack-protector=strong` resets `-C allow-partial-mitigations=stack-protector`. |
| index: usize, |
| allowed: Option<bool>, |
| } |
| |
| #[derive(Clone, Default)] |
| pub struct MitigationCoverageMap { |
| map: BTreeMap<DeniedPartialMitigationKind, MitigationStatus>, |
| } |
| |
| impl MitigationCoverageMap { |
| fn apply_mitigation( |
| &mut self, |
| kind: DeniedPartialMitigationKind, |
| index: usize, |
| allowed: Option<bool>, |
| ) { |
| self.map |
| .entry(kind) |
| .and_modify(|e| { |
| if index >= e.index { |
| *e = MitigationStatus { index, allowed } |
| } |
| }) |
| .or_insert(MitigationStatus { index, allowed }); |
| } |
| |
| pub(crate) fn handle_allowdeny_mitigation_option( |
| &mut self, |
| v: Option<&str>, |
| index: usize, |
| allowed: bool, |
| ) -> bool { |
| match v { |
| Some(s) => { |
| for sub in s.split(',') { |
| match sub.parse() { |
| Ok(kind) => self.apply_mitigation(kind, index, Some(allowed)), |
| Err(_) => return false, |
| } |
| } |
| true |
| } |
| None => false, |
| } |
| } |
| |
| pub(crate) fn reset_mitigation(&mut self, kind: DeniedPartialMitigationKind, index: usize) { |
| self.apply_mitigation(kind, index, None); |
| } |
| } |
| |
| pub struct DeniedPartialMitigationKindParseError; |
| |
| macro_rules! intersperse { |
| ($sep:expr, ($first:expr $(, $rest:expr)* $(,)?)) => { |
| concat!($first $(, $sep, $rest)*) |
| }; |
| } |
| |
| macro_rules! denied_partial_mitigations { |
| ([$self:ident] enum $kind:ident {$(($name:ident, $text:expr, $since:ident, $code:expr)),*}) => { |
| #[allow(non_camel_case_types)] |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, BlobDecodable)] |
| pub enum DeniedPartialMitigationKind { |
| $($name),* |
| } |
| |
| impl std::fmt::Display for DeniedPartialMitigationKind { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| match self { |
| $(DeniedPartialMitigationKind::$name => write!(f, $text)),* |
| } |
| } |
| } |
| |
| impl DeniedPartialMitigationKind { |
| pub(crate) const KINDS: &'static str = concat!("comma-separated list of mitigation kinds (available: ", |
| intersperse!(", ", ($(concat!("`", $text, "`")),*)), ")"); |
| } |
| |
| impl FromStr for DeniedPartialMitigationKind { |
| type Err = DeniedPartialMitigationKindParseError; |
| |
| fn from_str(v: &str) -> Result<DeniedPartialMitigationKind, DeniedPartialMitigationKindParseError> { |
| match v { |
| $($text => Ok(DeniedPartialMitigationKind::$name)),* |
| , |
| _ => Err(DeniedPartialMitigationKindParseError), |
| } |
| } |
| } |
| |
| #[allow(unused)] |
| impl DeniedPartialMitigationKind { |
| pub fn allowed_by_default_at(&self, edition: Edition) -> bool { |
| let denied_since = match self { |
| // Should change the denied-since edition of StackProtector to 2015 |
| // (all editions) when `-C stack-protector` is stabilized. |
| $(DeniedPartialMitigationKind::$name => Edition::$since),* |
| }; |
| edition < denied_since |
| } |
| } |
| |
| impl Options { |
| pub fn all_denied_partial_mitigations(&self) -> impl Iterator<Item = DeniedPartialMitigationKind> { |
| [$(DeniedPartialMitigationKind::$name),*].into_iter() |
| } |
| } |
| |
| impl Session { |
| pub fn gather_enabled_denied_partial_mitigations(&$self) -> Vec<DeniedPartialMitigation> { |
| let mut mitigations = [ |
| $( |
| DeniedPartialMitigation { |
| kind: DeniedPartialMitigationKind::$name, |
| level: From::from($code), |
| } |
| ),* |
| ]; |
| mitigations.sort(); |
| mitigations.into_iter().collect() |
| } |
| } |
| } |
| } |
| |
| denied_partial_mitigations! { |
| [self] |
| enum DeniedPartialMitigationKind { |
| // The mitigation name should match the option name in rustc_session::options, |
| // to allow for resetting the mitigation |
| (stack_protector, "stack-protector", EditionFuture, self.stack_protector()), |
| (control_flow_guard, "control-flow-guard", EditionFuture, self.opts.cg.control_flow_guard == CFGuard::Checks) |
| } |
| } |
| |
| /// A mitigation that cannot be partially enabled (see |
| /// [RFC 3855](https://github.com/rust-lang/rfcs/pull/3855)), but are currently enabled for this |
| /// crate. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Encodable, BlobDecodable)] |
| pub struct DeniedPartialMitigation { |
| pub kind: DeniedPartialMitigationKind, |
| pub level: DeniedPartialMitigationLevel, |
| } |
| |
| impl Options { |
| // Return the list of mitigations that are allowed to be partial |
| pub fn allowed_partial_mitigations( |
| &self, |
| edition: Edition, |
| ) -> impl Iterator<Item = DeniedPartialMitigationKind> { |
| let mut result: BTreeSet<_> = self |
| .all_denied_partial_mitigations() |
| .filter(|mitigation| mitigation.allowed_by_default_at(edition)) |
| .collect(); |
| for (kind, MitigationStatus { index: _, allowed }) in &self.mitigation_coverage_map.map { |
| match allowed { |
| Some(true) => { |
| result.insert(*kind); |
| } |
| Some(false) => { |
| result.remove(kind); |
| } |
| None => {} |
| } |
| } |
| result.into_iter() |
| } |
| } |