diff --git a/Cargo.lock b/Cargo.lock index c504b4234d..6c474f58a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,7 +454,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flowistry" version = "0.5.41" -source = "git+https://github.com/brownsys/flowistry?rev=a2ccfca2e6b5668ffd246eddc6abaf4d6e440a35#a2ccfca2e6b5668ffd246eddc6abaf4d6e440a35" +source = "git+https://github.com/brownsys/flowistry?rev=b9210041eb846ce88644a4a19569ed2afb26141c#b9210041eb846ce88644a4a19569ed2afb26141c" dependencies = [ "anyhow", "cfg-if", @@ -491,7 +491,7 @@ dependencies = [ "itertools 0.12.0", "log", "petgraph", - "rustc_utils 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=e990ded60afc928f76293fb9ad265c58405da1a7)", + "rustc_utils 0.7.4-nightly-2023-08-25 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -920,8 +920,8 @@ dependencies = [ "paralegal-spdg", "petgraph", "pretty", - "rustc_plugin", - "rustc_utils 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=e990ded60afc928f76293fb9ad265c58405da1a7)", + "rustc_plugin 0.7.4-nightly-2023-08-25 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_utils 0.7.4-nightly-2023-08-25 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_bare", "serial_test", @@ -1108,7 +1108,14 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc_plugin" version = "0.7.4-nightly-2023-08-25" -source = "git+https://github.com/JustusAdam/rustc_plugin?rev=e990ded60afc928f76293fb9ad265c58405da1a7#e990ded60afc928f76293fb9ad265c58405da1a7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1348edfa020dbe4807a4d99272332dadcbbedff6b587accb95faefe20d2c7129" +replace = "rustc_plugin 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864)" + +[[package]] +name = "rustc_plugin" +version = "0.7.4-nightly-2023-08-25" +source = "git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864#aa83f5740fa7eb5b8e3e1ee417b29536e87cc864" dependencies = [ "cargo_metadata", "log", @@ -1129,18 +1136,12 @@ name = "rustc_utils" version = "0.7.4-nightly-2023-08-25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09428c7086894369685cca54a516acc0f0ab6d0e5a628c094ba83bfddaf1aedf" -dependencies = [ - "anyhow", - "cfg-if", - "indexical", - "intervaltree", - "log", -] +replace = "rustc_utils 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864)" [[package]] name = "rustc_utils" version = "0.7.4-nightly-2023-08-25" -source = "git+https://github.com/JustusAdam/rustc_plugin?rev=e990ded60afc928f76293fb9ad265c58405da1a7#e990ded60afc928f76293fb9ad265c58405da1a7" +source = "git+https://github.com/JustusAdam/rustc_plugin?rev=aa83f5740fa7eb5b8e3e1ee417b29536e87cc864#aa83f5740fa7eb5b8e3e1ee417b29536e87cc864" dependencies = [ "anyhow", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 625b66952b..2c392735d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,22 +13,23 @@ indexical = "0.3.1" serde = "1.0.188" petgraph = { version = "0.6", features = ["serde-1"] } strum = { version = "0.25", features = ["derive"] } -# rustc_utils = { version = "=0.7.4-nightly-2023-08-25", features = [ -# "indexical", -# ] } -# rustc_plugin = "=0.7.4-nightly-2023-08-25" -rustc_utils = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "e990ded60afc928f76293fb9ad265c58405da1a7", features = [ +anyhow = { version = "1.0.72", features = ["backtrace"] } + +rustc_utils = { version = "=0.7.4-nightly-2023-08-25", features = [ "indexical", ] } -rustc_plugin = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "e990ded60afc928f76293fb9ad265c58405da1a7" } -#rustc_plugin = { path = "../rustc_plugin/crates/rustc_plugin" } -#rustc_utils = { path = "../rustc_plugin/crates/rustc_utils", features = ["indexical"] } -flowistry = { git = "https://github.com/brownsys/flowistry", rev = "a2ccfca2e6b5668ffd246eddc6abaf4d6e440a35", default-features = false } -anyhow = { version = "1.0.72", features = ["backtrace"] } +rustc_plugin = "=0.7.4-nightly-2023-08-25" +#flowistry = { path = "../flowistry/crates/flowistry", default-features = false } +flowistry = { git = "https://github.com/brownsys/flowistry", rev = "b9210041eb846ce88644a4a19569ed2afb26141c", default-features = false } [profile.release] debug = true -# [replace] +[replace] # "rustc_utils:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_utils" } # "rustc_plugin:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_plugin" } + +"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "aa83f5740fa7eb5b8e3e1ee417b29536e87cc864", features = [ + "indexical", +] } +"rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "aa83f5740fa7eb5b8e3e1ee417b29536e87cc864" } diff --git a/Makefile.toml b/Makefile.toml index 04afef64e2..7d201ae6e9 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -48,8 +48,6 @@ stage them and rerun the script until no more errors occur. dependencies = ["format-all", "clippy-all"] [tasks.analyzer-tests] -description = "Low-level tests for the PDG emitted by the analyzer specifically." -command = "cargo" args = [ "test", "-p", @@ -63,9 +61,13 @@ args = [ "--test", "new_alias_analysis_tests", "--test", + "boxes", + "--test", "async_tests", "--no-fail-fast", ] +description = "Low-level tests for the PDG emitted by the analyzer specifically." +command = "cargo" [tasks.policy-framework-tests] description = "Tests related to the correctness of the policy framework." diff --git a/crates/flowistry_pdg_construction/src/construct.rs b/crates/flowistry_pdg_construction/src/construct.rs index f06e82df38..3c33128134 100644 --- a/crates/flowistry_pdg_construction/src/construct.rs +++ b/crates/flowistry_pdg_construction/src/construct.rs @@ -714,7 +714,7 @@ impl<'tcx> GraphConstructor<'tcx> { ) -> Vec> { // Include all sources of indirection (each reference in the chain) as relevant places. let provenance = input - .refs_in_projection() + .refs_in_projection(self.place_info.body, self.place_info.tcx) .map(|(place_ref, _)| Place::from_ref(place_ref, self.tcx)); let inputs = iter::once(input).chain(provenance); @@ -794,19 +794,18 @@ impl<'tcx> GraphConstructor<'tcx> { ) -> Vec<(Place<'tcx>, DepNode<'tcx>)> { // **POINTER-SENSITIVITY:** // If `mutated` involves indirection via dereferences, then resolve it to the direct places it could point to. - let aliases = self.aliases(mutated).collect_vec(); + let aliases = self.aliases(mutated); // **FIELD-SENSITIVITY:** we do NOT deal with fields on *writes* (in this function), // only on *reads* (in `add_input_to_op`). // For each mutated `dst`: aliases - .iter() .map(|dst| { // Create a destination node for (DST @ CURRENT_LOC). ( - *dst, - DepNode::new(*dst, self.make_call_string(location), self.tcx, &self.body), + dst, + DepNode::new(dst, self.make_call_string(location), self.tcx, &self.body), ) }) .collect() diff --git a/crates/flowistry_pdg_construction/src/mutation.rs b/crates/flowistry_pdg_construction/src/mutation.rs index f9b72b655d..46ae966e76 100644 --- a/crates/flowistry_pdg_construction/src/mutation.rs +++ b/crates/flowistry_pdg_construction/src/mutation.rs @@ -234,7 +234,7 @@ where // reference, except for the provenance of reborrows. Rvalue::Ref(_, _, place) => { let inputs = place - .refs_in_projection() + .refs_in_projection(self.place_info.body, self.place_info.tcx) .map(|(place_ref, _)| (Place::from_ref(place_ref, tcx), None)) .collect::>(); (self.f)( diff --git a/crates/paralegal-flow/src/ann/db.rs b/crates/paralegal-flow/src/ann/db.rs index d11541a6b7..1b37e4e73c 100644 --- a/crates/paralegal-flow/src/ann/db.rs +++ b/crates/paralegal-flow/src/ann/db.rs @@ -412,17 +412,7 @@ impl<'tcx> MarkerCtx<'tcx> { ) }; - let include_type_markers = - self.0.config.local_function_type_marking() || !function.def_id().is_local(); - direct_markers.chain( - if include_type_markers { - get_type_markers() - } else { - None - } - .into_iter() - .flatten(), - ) + direct_markers.chain(get_type_markers().into_iter().flatten()) } /// Iterate over all discovered annotations, whether local or external @@ -463,7 +453,7 @@ pub struct MarkerDatabase<'tcx> { /// Cache whether markers are reachable transitively. reachable_markers: Cache, Box<[Identifier]>>, /// Configuration options - config: &'static MarkerControl, + _config: &'static MarkerControl, type_markers: Cache, Box>, } @@ -475,7 +465,7 @@ impl<'tcx> MarkerDatabase<'tcx> { local_annotations: HashMap::default(), external_annotations: resolve_external_markers(args, tcx), reachable_markers: Default::default(), - config: args.marker_control(), + _config: args.marker_control(), type_markers: Default::default(), } } @@ -530,7 +520,7 @@ type RawExternalMarkers = HashMap>; /// Given the TOML of external annotations we have parsed, resolve the paths /// (keys of the map) to [`DefId`]s. fn resolve_external_markers(opts: &Args, tcx: TyCtxt) -> ExternalMarkers { - if let Some(annotation_file) = opts.modelctrl().external_annotations() { + if let Some(annotation_file) = opts.marker_control().external_annotations() { let from_toml: RawExternalMarkers = toml::from_str( &std::fs::read_to_string(annotation_file).unwrap_or_else(|_| { panic!( diff --git a/crates/paralegal-flow/src/args.rs b/crates/paralegal-flow/src/args.rs index 3a79b1859a..ddbe7553c8 100644 --- a/crates/paralegal-flow/src/args.rs +++ b/crates/paralegal-flow/src/args.rs @@ -12,6 +12,7 @@ use anyhow::Error; use clap::ValueEnum; use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; use crate::utils::TinyBitSet; use crate::{num_derive, num_traits::FromPrimitive}; @@ -48,7 +49,6 @@ impl TryFrom for Args { target, abort_after_analysis, mut anactrl, - modelctrl, dump, marker_control, cargo_args, @@ -98,7 +98,6 @@ impl TryFrom for Args { target, abort_after_analysis, anactrl: anactrl.try_into()?, - modelctrl, dump, build_config, marker_control, @@ -133,8 +132,6 @@ pub struct Args { marker_control: MarkerControl, /// Additional arguments that control the flow analysis specifically anactrl: AnalysisCtrl, - /// Additional arguments that control the generation and composition of the model - modelctrl: ModelCtrl, /// Additional arguments that control debug output specifically dump: DumpArgs, /// Additional configuration for the build process/rustc @@ -143,6 +140,25 @@ pub struct Args { cargo_args: Vec, } +impl Default for Args { + fn default() -> Self { + Self { + verbosity: log::LevelFilter::Info, + log_level_config: LogLevelConfig::Disabled, + result_path: PathBuf::from(paralegal_spdg::FLOW_GRAPH_OUT_NAME), + relaxed: false, + target: None, + abort_after_analysis: false, + marker_control: Default::default(), + anactrl: Default::default(), + dump: Default::default(), + build_config: Default::default(), + cargo_args: Vec::new(), + attach_to_debugger: None, + } + } +} + /// Arguments as exposed on the command line. /// /// You should then use `try_into` to convert this to [`Args`], the argument @@ -185,9 +201,6 @@ pub struct ClapArgs { /// Additional arguments which control marker assignment and discovery #[clap(flatten, next_help_heading = "Marker Control")] marker_control: MarkerControl, - /// Additional arguments that control the generation and composition of the model - #[clap(flatten, next_help_heading = "Model Generation")] - modelctrl: ModelCtrl, /// Additional arguments that control debug args specifically #[clap(flatten)] dump: ParseableDumpArgs, @@ -253,7 +266,7 @@ impl From for DumpArgs { /// cli, internally we use the snake-case version of the option as a method on /// this type. This is so we can rename the outer UI without breaking code or /// even combine options together. -#[derive(serde::Serialize, serde::Deserialize, Clone)] +#[derive(serde::Serialize, serde::Deserialize, Clone, Default)] pub struct DumpArgs(TinyBitSet); impl DumpArgs { @@ -331,9 +344,6 @@ impl Args { &self.anactrl } - pub fn modelctrl(&self) -> &ModelCtrl { - &self.modelctrl - } /// the file to write results to pub fn result_path(&self) -> &std::path::Path { self.result_path.as_path() @@ -366,8 +376,8 @@ impl Args { } } -#[derive(serde::Serialize, serde::Deserialize, clap::Args)] -pub struct ModelCtrl { +#[derive(serde::Serialize, serde::Deserialize, clap::Args, Default)] +pub struct MarkerControl { /// A JSON file from which to load additional annotations. Whereas normally /// annotation can only be placed on crate-local items, these can also be /// placed on third party items, such as functions from the stdlib. @@ -381,30 +391,12 @@ pub struct ModelCtrl { external_annotations: Option, } -impl ModelCtrl { +impl MarkerControl { pub fn external_annotations(&self) -> Option<&std::path::Path> { self.external_annotations.as_deref() } } -/// Arguments which control marker assignment and discovery -#[derive(serde::Serialize, serde::Deserialize, clap::Args)] -pub struct MarkerControl { - /// Don't mark the outputs of local functions if they are of a marked type. - /// - /// Be aware that disabling this can cause unsoundness as inline - /// construction of such types will not be emitted into the model. A warning - /// is however emitted in that case. - #[clap(long, env = "PARALEGAL_NO_LOCAL_FUNCTION_TYPE_MARKING")] - no_local_function_type_marking: bool, -} - -impl MarkerControl { - pub fn local_function_type_marking(&self) -> bool { - !self.no_local_function_type_marking - } -} - /// Arguments that control the flow analysis #[derive(clap::Args)] struct ClapAnalysisCtrl { @@ -442,6 +434,15 @@ pub struct AnalysisCtrl { inlining_depth: InliningDepth, } +impl Default for AnalysisCtrl { + fn default() -> Self { + Self { + analyze: Vec::new(), + inlining_depth: InliningDepth::Adaptive, + } + } +} + impl TryFrom for AnalysisCtrl { type Error = Error; fn try_from(value: ClapAnalysisCtrl) -> Result { diff --git a/crates/paralegal-flow/src/lib.rs b/crates/paralegal-flow/src/lib.rs index 9a6cac8b4c..ddae56114e 100644 --- a/crates/paralegal-flow/src/lib.rs +++ b/crates/paralegal-flow/src/lib.rs @@ -63,7 +63,7 @@ pub mod rust { } use args::{ClapArgs, Debugger, LogLevelConfig}; -use desc::utils::write_sep; +use desc::{utils::write_sep, ProgramDescription}; use rust::*; use rustc_plugin::CrateFilter; @@ -94,7 +94,7 @@ pub mod test_utils; pub use paralegal_spdg as desc; pub use crate::ann::db::MarkerCtx; -pub use args::{AnalysisCtrl, Args, BuildConfig, DepConfig, DumpArgs, ModelCtrl}; +pub use args::{AnalysisCtrl, Args, BuildConfig, DepConfig, DumpArgs, MarkerControl}; use crate::{ stats::{Stats, TimedStat}, @@ -136,6 +136,21 @@ struct NoopCallbacks {} impl rustc_driver::Callbacks for NoopCallbacks {} +impl Callbacks { + pub fn run(&self, tcx: TyCtxt) -> anyhow::Result { + tcx.sess.abort_if_errors(); + discover::CollectingVisitor::new(tcx, self.opts, self.stats.clone()).run() + } + + pub fn new(opts: &'static Args) -> Self { + Self { + opts, + stats: Default::default(), + start: Instant::now(), + } + } +} + impl rustc_driver::Callbacks for Callbacks { fn config(&mut self, config: &mut rustc_interface::Config) { config.override_queries = Some(borrowck_facts::override_queries); @@ -157,9 +172,7 @@ impl rustc_driver::Callbacks for Callbacks { .global_ctxt() .unwrap() .enter(|tcx| { - tcx.sess.abort_if_errors(); - let desc = - discover::CollectingVisitor::new(tcx, self.opts, self.stats.clone()).run()?; + let desc = self.run(tcx)?; info!("All elems walked"); tcx.sess.abort_if_errors(); @@ -365,15 +378,7 @@ impl rustc_plugin::RustcPlugin for DfppPlugin { "Arguments: {}", Print(|f| write_sep(f, " ", &compiler_args, Display::fmt)) ); - rustc_driver::RunCompiler::new( - &compiler_args, - &mut Callbacks { - opts, - stats: Default::default(), - start: Instant::now(), - }, - ) - .run() + rustc_driver::RunCompiler::new(&compiler_args, &mut Callbacks::new(opts)).run() } } diff --git a/crates/paralegal-flow/src/utils/mod.rs b/crates/paralegal-flow/src/utils/mod.rs index 31491edaea..2509353063 100644 --- a/crates/paralegal-flow/src/utils/mod.rs +++ b/crates/paralegal-flow/src/utils/mod.rs @@ -3,8 +3,6 @@ extern crate smallvec; use thiserror::Error; -use smallvec::SmallVec; - use crate::{ desc::Identifier, rust::{ @@ -472,29 +470,10 @@ impl<'tcx> Overlap<'tcx> { /// Extension trait for [`Place`]s so we can implement methods on them. [`Self`] /// is only ever supposed to be instantiated as [`Place`]. pub trait PlaceExt<'tcx> { - /// Constructs a set of places that are ref/deref/field un-layerings of the - /// input place. - /// - /// The ordering is starting with the place itself, then successively removing - /// layers until only the local is left. E.g. `provenance_of(_1.foo.bar) == - /// [_1.foo.bar, _1.foo, _1]` - fn provenance(self, tcx: TyCtxt<'tcx>) -> SmallVec<[Place<'tcx>; 2]>; - fn simple_overlaps(self, other: Place<'tcx>) -> Overlap<'tcx>; } impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { - fn provenance(self, tcx: TyCtxt<'tcx>) -> SmallVec<[Place<'tcx>; 2]> { - use rustc_utils::PlaceExt; - let mut refs: SmallVec<_> = self - .refs_in_projection() - .map(|(ptr, _)| Place::from_ref(ptr, tcx)) - .chain([self]) - .collect(); - refs.reverse(); - refs - } - fn simple_overlaps(self, other: Place<'tcx>) -> Overlap<'tcx> { if self.local != other.local || self diff --git a/crates/paralegal-flow/tests/boxes.rs b/crates/paralegal-flow/tests/boxes.rs new file mode 100644 index 0000000000..71d3b4e648 --- /dev/null +++ b/crates/paralegal-flow/tests/boxes.rs @@ -0,0 +1,103 @@ +#![feature(rustc_private)] +#[macro_use] +extern crate lazy_static; + +use paralegal_flow::test_utils::*; +use paralegal_spdg::Identifier; + +const CRATE_DIR: &str = "tests/boxes"; + +lazy_static! { + static ref TEST_CRATE_ANALYZED: bool = run_paralegal_flow_with_flow_graph_dump(CRATE_DIR); +} + +macro_rules! define_test { + ($($t:tt)*) => { + paralegal_flow::define_flow_test_template!(TEST_CRATE_ANALYZED, CRATE_DIR, $($t)*); + }; +} + +define_test!(simple_overtaint: graph -> { + let sources = graph.marked(Identifier::new_intern("source")); + let mid = graph.marked(Identifier::new_intern("checkpoint")); + let end = graph.marked(Identifier::new_intern("sink")); + assert!(!sources.is_empty()); + assert!(!mid.is_empty()); + assert!(!end.is_empty()); + assert!(sources.always_happens_before_data(&mid, &end)); +}); + +define_test!(ref_with_checkpoint: graph -> { + let sources = graph.marked(Identifier::new_intern("source")); + let mid = graph.marked(Identifier::new_intern("checkpoint_2")); + let end = graph.marked(Identifier::new_intern("sink")); + let modifier = graph.marked(Identifier::new_intern("modifier")); + assert!(!sources.is_empty()); + assert!(!mid.is_empty()); + assert!(!end.is_empty()); + assert!(!modifier.is_empty()); + assert!(modifier.flows_to_data(&end)); + assert!(sources.flows_to_data(&end)); + assert!(!mid.always_happens_before_data(&modifier, &end)); +}); + +// This one is just to check that fields have the same behavior as boxes. +define_test!(field_ref: graph -> { + let sources = graph.marked(Identifier::new_intern("source_2")); + let end = graph.marked(Identifier::new_intern("sink")); + let modifier = graph.marked(Identifier::new_intern("modifier")); + assert!(!sources.is_empty()); + assert!(!end.is_empty()); + assert!(!modifier.is_empty()); + assert!(modifier.flows_to_data(&end)); + assert!(sources.flows_to_data(&end)); + assert!(!sources.always_happens_before_data(&modifier, &end)); +}); + +define_test!(ref_mut_box: graph -> { + let sources = graph.marked(Identifier::new_intern("source_2")); + let end = graph.marked(Identifier::new_intern("sink")); + let modifier = graph.marked(Identifier::new_intern("modifier")); + assert!(!sources.is_empty()); + assert!(!end.is_empty()); + assert!(!modifier.is_empty()); + assert!(modifier.flows_to_data(&end)); + assert!(sources.flows_to_data(&end)); + assert!(!sources.always_happens_before_data(&modifier, &end)); +}); + +define_test!(box_ref_mut: graph -> { + let sources = graph.marked(Identifier::new_intern("source_2")); + let end = graph.marked(Identifier::new_intern("sink")); + let modifier = graph.marked(Identifier::new_intern("modifier")); + assert!(!sources.is_empty()); + assert!(!end.is_empty()); + assert!(!modifier.is_empty()); + assert!(modifier.flows_to_data(&end)); + assert!(sources.flows_to_data(&end)); + assert!(!sources.always_happens_before_data(&modifier, &end)); +}); + +define_test!(strong_box_update skip "Box modification is not currently considered strong. See https://github.com/brownsys/paralegal/issues/155": graph -> { + let sources = graph.marked(Identifier::new_intern("source_2")); + let end = graph.marked(Identifier::new_intern("sink")); + let modifier = graph.marked(Identifier::new_intern("modifier")); + assert!(!sources.is_empty()); + assert!(!end.is_empty()); + assert!(!modifier.is_empty()); + assert!(modifier.flows_to_data(&end)); + assert!(!sources.flows_to_data(&end)); + //assert!(!sources.always_happens_before_data(&modifier, &end)); +}); + +define_test!(strong_ref_in_box_update: graph -> { + let sources = graph.marked(Identifier::new_intern("source_2")); + let end = graph.marked(Identifier::new_intern("sink")); + let modifier = graph.marked(Identifier::new_intern("modifier")); + assert!(!sources.is_empty()); + assert!(!end.is_empty()); + assert!(!modifier.is_empty()); + assert!(modifier.flows_to_data(&end)); + assert!(!sources.flows_to_data(&end)); + //assert!(!sources.always_happens_before_data(&modifier, &end)); +}); diff --git a/crates/paralegal-flow/tests/boxes/Cargo.lock b/crates/paralegal-flow/tests/boxes/Cargo.lock new file mode 100644 index 0000000000..e9c069d3a6 --- /dev/null +++ b/crates/paralegal-flow/tests/boxes/Cargo.lock @@ -0,0 +1,49 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "boxes" +version = "0.1.0" +dependencies = [ + "paralegal", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "paralegal" +version = "0.1.0" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/crates/paralegal-flow/tests/boxes/Cargo.toml b/crates/paralegal-flow/tests/boxes/Cargo.toml new file mode 100644 index 0000000000..870110de51 --- /dev/null +++ b/crates/paralegal-flow/tests/boxes/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "boxes" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +paralegal = { path = "../../../paralegal" } diff --git a/crates/paralegal-flow/tests/boxes/src/main.rs b/crates/paralegal-flow/tests/boxes/src/main.rs new file mode 100644 index 0000000000..ea85db85ab --- /dev/null +++ b/crates/paralegal-flow/tests/boxes/src/main.rs @@ -0,0 +1,84 @@ +type F = usize; +#[paralegal::marker(source, return)] +fn source() -> Box { + unreachable!() +} + +#[paralegal::marker(checkpoint, return)] +fn checkpoint(_: T) -> Box { + unreachable!() +} + +#[paralegal::marker(sink, arguments = [0])] +fn sink(_: T) {} + +#[paralegal::analyze] +fn simple_overtaint() { + sink(checkpoint(source())) +} + +#[paralegal::marker(checkpoint_2, return)] +fn checkpoint_2(i: T) -> Box { + Box::new(i) +} + +#[paralegal::marker(modifier, return)] +fn modifier() -> usize { + 6 +} + +#[paralegal::analyze] +fn ref_with_checkpoint() { + let mut inp = source(); + let r = checkpoint_2(&mut inp); + ***r += modifier(); + sink(inp); +} + +#[paralegal::analyze] +fn strong_box_update() { + let mut inp = Box::new(source_2()); + let r = &mut inp; + **r = modifier(); + sink(inp); +} + +#[paralegal::analyze] +fn strong_ref_in_box_update() { + let mut src = source_2(); + let mut inp = Box::new(&mut src); + **inp = modifier(); + sink(src); +} + +#[paralegal::marker(source_2, return)] +fn source_2() -> usize { + 0 +} + +#[paralegal::analyze] +fn field_ref() { + let mut inp = (source_2(),); + let my_ref = &mut inp; + + my_ref.0 += modifier(); + sink(inp); +} + +#[paralegal::analyze] +fn ref_mut_box() { + let mut inp = Box::new(source_2()); + let my_ref = &mut inp; + + **my_ref += modifier(); + sink(inp); +} + +#[paralegal::analyze] +fn box_ref_mut() { + let mut src = source_2(); + let mut inp = Box::new(&mut src); + + **inp += modifier(); + sink(src); +} diff --git a/crates/paralegal-policy/tests/box.rs b/crates/paralegal-policy/tests/box.rs new file mode 100644 index 0000000000..ed77f9639b --- /dev/null +++ b/crates/paralegal-policy/tests/box.rs @@ -0,0 +1,58 @@ +use std::sync::Arc; + +use anyhow::Result; +use helpers::Test; +use paralegal_policy::{algo::ahb, assert_error, Context}; +use paralegal_spdg::{HashSet, Identifier}; + +mod helpers; + +fn simple_policy(ctx: Arc) -> Result<()> { + let sources = ctx + .nodes_marked_any_way(Identifier::new_intern("source")) + .collect::>(); + let checkpoints = ctx + .nodes_marked_any_way(Identifier::new_intern("checkpoint")) + .collect::>(); + let sinks = ctx + .nodes_marked_any_way(Identifier::new_intern("sink")) + .collect::>(); + assert_error!(ctx, !sinks.is_empty()); + assert_error!(ctx, !checkpoints.is_empty()); + assert_error!(ctx, !sources.is_empty()); + ctx.always_happens_before( + sources.iter().copied(), + |a| checkpoints.contains(&a), + |t| sinks.contains(&t), + )? + .report(ctx.clone()); + Ok(()) +} + +#[test] +fn box_overtaint() -> Result<()> { + let mut test = Test::new(stringify!( + type F = usize; + #[paralegal::marker(source, return)] + fn source() -> Box { + unreachable!() + } + + #[paralegal::marker(checkpoint, return)] + fn checkpoint(_: T) -> Box { + unreachable!() + } + + #[paralegal::marker(sink, arguments = [0])] + fn sink(_: T) {} + + #[paralegal::analyze] + fn main() { + sink(checkpoint(source())) + } + ))?; + + test.context_config().always_happens_before_tracing = ahb::TraceLevel::Full; + + test.run(simple_policy) +}