diff --git a/Cargo.lock b/Cargo.lock index 901c681d70..fd384380c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,12 +1149,12 @@ name = "rustc_plugin" version = "0.7.4-nightly-2023-08-25" 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=d7bde3ae16a7137de594c2dd811030cd761a8993)" +replace = "rustc_plugin 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=e413907b2ae9a03d2c8e9aca3b72dd451a16b1db)" [[package]] name = "rustc_plugin" version = "0.7.4-nightly-2023-08-25" -source = "git+https://github.com/JustusAdam/rustc_plugin?rev=d7bde3ae16a7137de594c2dd811030cd761a8993#d7bde3ae16a7137de594c2dd811030cd761a8993" +source = "git+https://github.com/JustusAdam/rustc_plugin?rev=e413907b2ae9a03d2c8e9aca3b72dd451a16b1db#e413907b2ae9a03d2c8e9aca3b72dd451a16b1db" dependencies = [ "cargo_metadata", "log", @@ -1175,12 +1175,12 @@ name = "rustc_utils" version = "0.7.4-nightly-2023-08-25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09428c7086894369685cca54a516acc0f0ab6d0e5a628c094ba83bfddaf1aedf" -replace = "rustc_utils 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=d7bde3ae16a7137de594c2dd811030cd761a8993)" +replace = "rustc_utils 0.7.4-nightly-2023-08-25 (git+https://github.com/JustusAdam/rustc_plugin?rev=e413907b2ae9a03d2c8e9aca3b72dd451a16b1db)" [[package]] name = "rustc_utils" version = "0.7.4-nightly-2023-08-25" -source = "git+https://github.com/JustusAdam/rustc_plugin?rev=d7bde3ae16a7137de594c2dd811030cd761a8993#d7bde3ae16a7137de594c2dd811030cd761a8993" +source = "git+https://github.com/JustusAdam/rustc_plugin?rev=e413907b2ae9a03d2c8e9aca3b72dd451a16b1db#e413907b2ae9a03d2c8e9aca3b72dd451a16b1db" dependencies = [ "anyhow", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index d4306213fb..b3ef519232 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,9 +35,9 @@ debug = true [replace."rustc_utils:0.7.4-nightly-2023-08-25"] # path = "../rustc_plugin/crates/rustc_utils" git = "https://github.com/JustusAdam/rustc_plugin" -rev = "d7bde3ae16a7137de594c2dd811030cd761a8993" +rev = "e413907b2ae9a03d2c8e9aca3b72dd451a16b1db" [replace."rustc_plugin:0.7.4-nightly-2023-08-25"] # path = "../rustc_plugin/crates/rustc_plugin" git = "https://github.com/JustusAdam/rustc_plugin" -rev = "d7bde3ae16a7137de594c2dd811030cd761a8993" +rev = "e413907b2ae9a03d2c8e9aca3b72dd451a16b1db" diff --git a/crates/flowistry_pdg_construction/src/async_support.rs b/crates/flowistry_pdg_construction/src/async_support.rs index 38a7c1eb6c..6f39fed871 100644 --- a/crates/flowistry_pdg_construction/src/async_support.rs +++ b/crates/flowistry_pdg_construction/src/async_support.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use either::Either; use itertools::Itertools; use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_middle::{ mir::{ AggregateKind, BasicBlock, Body, Location, Operand, Place, Rvalue, Statement, @@ -12,6 +12,8 @@ use rustc_middle::{ ty::{GenericArgsRef, Instance, TyCtxt}, }; +use crate::utils::is_async; + use super::{ local_analysis::{CallKind, LocalAnalysis}, utils, @@ -63,7 +65,7 @@ pub fn try_as_async_trait_function<'tcx>( tcx: TyCtxt, def_id: DefId, body: &Body<'tcx>, -) -> Option<(LocalDefId, GenericArgsRef<'tcx>, Location)> { +) -> Option<(DefId, GenericArgsRef<'tcx>, Location)> { if !has_async_trait_signature(tcx, def_id) { return None; } @@ -75,7 +77,7 @@ pub fn try_as_async_trait_function<'tcx>( move |(statement_index, statement)| { let (def_id, generics) = match_async_trait_assign(statement)?; Some(( - def_id.as_local()?, + def_id, generics, Location { block, @@ -147,7 +149,7 @@ fn match_pin_box_dyn_ty(lang_items: &rustc_hir::LanguageItems, t: ty::Ty) -> boo }) } -fn get_async_generator<'tcx>(body: &Body<'tcx>) -> (LocalDefId, GenericArgsRef<'tcx>, Location) { +fn get_async_generator<'tcx>(body: &Body<'tcx>) -> (DefId, GenericArgsRef<'tcx>, Location) { let block = BasicBlock::from_usize(0); let location = Location { block, @@ -163,7 +165,7 @@ fn get_async_generator<'tcx>(body: &Body<'tcx>) -> (LocalDefId, GenericArgsRef<' else { panic!("Async fn should assign to a generator") }; - (def_id.expect_local(), generic_args, location) + (*def_id, generic_args, location) } /// Try to interpret this function as an async function. @@ -176,7 +178,7 @@ pub fn determine_async<'tcx>( def_id: DefId, body: &Body<'tcx>, ) -> Option<(Instance<'tcx>, Location, AsyncType)> { - let ((generator_def_id, args, loc), asyncness) = if tcx.asyncness(def_id).is_async() { + let ((generator_def_id, args, loc), asyncness) = if is_async(tcx, def_id) { (get_async_generator(body), AsyncType::Fn) } else { ( @@ -185,8 +187,7 @@ pub fn determine_async<'tcx>( ) }; let param_env = tcx.param_env_reveal_all_normalized(def_id); - let generator_fn = - utils::try_resolve_function(tcx, generator_def_id.to_def_id(), param_env, args)?; + let generator_fn = utils::try_resolve_function(tcx, generator_def_id, param_env, args)?; Some((generator_fn, loc, asyncness)) } diff --git a/crates/flowistry_pdg_construction/src/calling_convention.rs b/crates/flowistry_pdg_construction/src/calling_convention.rs index 4ea901245c..dbb121b753 100644 --- a/crates/flowistry_pdg_construction/src/calling_convention.rs +++ b/crates/flowistry_pdg_construction/src/calling_convention.rs @@ -11,16 +11,59 @@ use rustc_middle::{ use crate::{async_support::AsyncInfo, local_analysis::CallKind, utils}; +/// Describes how the formal parameters of a given function call relate to the +/// actual parameters. #[derive(Debug)] pub enum CallingConvention<'tcx> { + /// 1 to 1 mapping Direct(Box<[Operand<'tcx>]>), + /// First argument is the closed-over environment, second argument is a + /// tuple that contains the actual argument to the call of the closure + /// function. Indirect { + once_shim: bool, closure_arg: Operand<'tcx>, tupled_arguments: Operand<'tcx>, }, + /// An async generator, only has one argument which is the generator state. Async(Place<'tcx>), } +/// The result of calculating a translation from a child place (in a called +/// function) to a parent place (in the caller). +/// +/// This is partially translated and thus allows us to either complete the +/// translation to a precise parent place ([`Self::make_translated_place`]), +/// e.g. one that corresponds to the child 1-1, or to just use the parent place, +/// for strategic overtaint, e.g. discarding the child projections +/// ([`Self::base_place`]). +pub struct PlaceTranslation<'a, 'tcx> { + new_base: Place<'tcx>, + additional_projection: &'tcx [PlaceElem<'tcx>], + scope: &'a PlaceTranslator<'a, 'tcx>, +} + +impl<'a, 'tcx> PlaceTranslation<'a, 'tcx> { + /// Complete the translation and return a precise parent place. + pub fn make_translated_place(&self) -> Place<'tcx> { + let base_place_projected = self + .new_base + .project_deeper(self.additional_projection, self.scope.tcx); + trace!(" ⮑ Translated to: {base_place_projected:?}"); + utils::retype_place( + base_place_projected, + self.scope.tcx, + self.scope.parent_body, + self.scope.parent_body_def_id, + ) + } + + /// Return the base version of the parent place with no child projections applied. + pub fn base_place(&self) -> Place<'tcx> { + self.new_base + } +} + impl<'tcx> CallingConvention<'tcx> { pub fn from_call_kind( kind: &CallKind<'tcx>, @@ -29,71 +72,109 @@ impl<'tcx> CallingConvention<'tcx> { match kind { CallKind::AsyncPoll(poll) => CallingConvention::Async(poll.generator_data), CallKind::Direct => CallingConvention::Direct(args.into()), - CallKind::Indirect => CallingConvention::Indirect { + CallKind::Indirect { once_shim } => CallingConvention::Indirect { + once_shim: *once_shim, closure_arg: args[0].clone(), tupled_arguments: args[1].clone(), }, } } +} - pub(crate) fn translate_to_parent( - &self, - child: Place<'tcx>, - async_info: &AsyncInfo, +/// This struct represents all the information necessary to translate places +/// from a child (the callee) to its parent (caller) at the boundary of a +/// particular function call. +pub struct PlaceTranslator<'a, 'tcx> { + async_info: &'a AsyncInfo, + parent_body_def_id: DefId, + parent_body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + destination: Place<'tcx>, + calling_convention: &'a CallingConvention<'tcx>, + /// Governs whether the translation produces precise results (1-1 + /// child-parent translations) or approximate one's (discarding child + /// projections). + precise: bool, +} + +impl<'a, 'tcx> PlaceTranslator<'a, 'tcx> { + /// `destination` must be the place to which the return is assigned in the + /// parent (caller). + /// + /// The `precise` parameter governs whether the translation produces precise + /// results (1-1 child-parent translations) or approximate one's (discarding + /// child projections). + pub(crate) fn new( + async_info: &'a AsyncInfo, + parent_body_def_id: DefId, + parent_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, - parent_body: &Body<'tcx>, - parent_def_id: DefId, destination: Place<'tcx>, - ) -> Option> { - trace!(" Translating child place: {child:?}"); - let (parent_place, child_projection) = - self.handle_translate(async_info, tcx, child, destination, parent_body)?; - - let parent_place_projected = parent_place.project_deeper(child_projection, tcx); - trace!(" ⮑ Translated to: {parent_place_projected:?}"); - Some(utils::retype_place( - parent_place_projected, - tcx, + calling_convention: &'a CallingConvention<'tcx>, + precise: bool, + ) -> Self { + Self { + async_info, parent_body, - parent_def_id, - )) + parent_body_def_id, + tcx, + destination, + calling_convention, + precise, + } } - pub(crate) fn handle_translate( - &self, - async_info: &AsyncInfo, - tcx: TyCtxt<'tcx>, + /// Returns a fully translated parent place. If `self.precise == true` this + /// place will be a precise 1-1 translation, otherwise just the base parent + /// place. + /// + /// Returns `None` if the input child cannot be represented in the parent. + pub(crate) fn translate_to_parent(&self, child: Place<'tcx>) -> Option> { + let translation = self.handle_translate(child)?; + Some(if self.precise { + translation.make_translated_place() + } else { + translation.base_place() + }) + } + + /// Returns a calculated translation that needs to be finished. + /// + /// Returns `None` if the input child cannot be represented in the parent. + pub(crate) fn handle_translate<'b>( + &'b self, child: Place<'tcx>, - destination: Place<'tcx>, - parent_body: &Body<'tcx>, - ) -> Option<(Place<'tcx>, &[PlaceElem<'tcx>])> { - let result = match self { + ) -> Option> { + let (new_base, additional_projection) = match self.calling_convention { // Async return must be handled special, because it gets wrapped in `Poll::Ready` - Self::Async { .. } if child.local == RETURN_PLACE => { - let in_poll = destination.project_deeper( - &[PlaceElem::Downcast(None, async_info.poll_ready_variant_idx)], - tcx, + CallingConvention::Async { .. } if child.local == RETURN_PLACE => { + let in_poll = self.destination.project_deeper( + &[PlaceElem::Downcast( + None, + self.async_info.poll_ready_variant_idx, + )], + self.tcx, ); - let field_idx = async_info.poll_ready_field_idx; + let field_idx = self.async_info.poll_ready_field_idx; let child_inner_return_type = in_poll - .ty(parent_body.local_decls(), tcx) - .field_ty(tcx, field_idx); + .ty(self.parent_body.local_decls(), self.tcx) + .field_ty(self.tcx, field_idx); ( in_poll.project_deeper( &[PlaceElem::Field(field_idx, child_inner_return_type)], - tcx, + self.tcx, ), &child.projection[..], ) } - _ if child.local == RETURN_PLACE => (destination, &child.projection[..]), + _ if child.local == RETURN_PLACE => (self.destination, &child.projection[..]), // Map arguments to the argument array - Self::Direct(args) => ( + CallingConvention::Direct(args) => ( args[child.local.as_usize() - 1].place()?, &child.projection[..], ), // Map arguments to projections of the future, the poll's first argument - Self::Async(ctx) => { + CallingConvention::Async(ctx) => { if child.local.as_usize() == 1 { (*ctx, &child.projection[..]) } else { @@ -102,24 +183,52 @@ impl<'tcx> CallingConvention<'tcx> { } // Map closure captures to the first argument. // Map formal parameters to the second argument. - Self::Indirect { + CallingConvention::Indirect { + once_shim, closure_arg, tupled_arguments, } => { if child.local.as_usize() == 1 { - (closure_arg.place()?, &child.projection[..]) + // Accounting for shims + let next_idx = if *once_shim { + // If this is a once shim then the signature of the + // function and its call don't match fully. (We are + // calling a closure that takes it's `self` by reference + // with a `self` by value.) + if let Some(fst) = child.projection.first() { + // If there is a first place it must be a deref + assert_eq!(fst, &PlaceElem::Deref); + } else { + // We cannot remap the raw first place as it is a + // reference that does not exist in the caller (as + // the caller passes `self` by value.) + return None; + } + // We skip the first projection element (a deref) to + // account for the difference in signature + 1 + } else { + 0 + }; + (closure_arg.place()?, &child.projection[next_idx..]) } else { let tuple_arg = tupled_arguments.place()?; let _projection = child.projection.to_vec(); let field = FieldIdx::from_usize(child.local.as_usize() - 2); - let field_ty = tuple_arg.ty(parent_body, tcx).field_ty(tcx, field); + let field_ty = tuple_arg + .ty(self.parent_body, self.tcx) + .field_ty(self.tcx, field); ( - tuple_arg.project_deeper(&[PlaceElem::Field(field, field_ty)], tcx), + tuple_arg.project_deeper(&[PlaceElem::Field(field, field_ty)], self.tcx), &child.projection[..], ) } } }; - Some(result) + Some(PlaceTranslation { + new_base, + additional_projection, + scope: self, + }) } } diff --git a/crates/flowistry_pdg_construction/src/construct.rs b/crates/flowistry_pdg_construction/src/construct.rs index 07e0da811c..7a3bb7ae7e 100644 --- a/crates/flowistry_pdg_construction/src/construct.rs +++ b/crates/flowistry_pdg_construction/src/construct.rs @@ -22,6 +22,7 @@ use rustc_utils::cache::Cache; use crate::{ async_support::*, body_cache::{self, BodyCache, CachedBody}, + calling_convention::PlaceTranslator, graph::{ push_call_string_root, DepEdge, DepGraph, DepNode, PartialGraph, SourceUse, TargetUse, }, @@ -342,11 +343,12 @@ impl<'tcx> PartialGraph<'tcx> { return false; }; - let (child_descriptor, calling_convention) = match handling { + let (child_descriptor, calling_convention, precise) = match handling { CallHandling::Ready { calling_convention, descriptor, - } => (descriptor, calling_convention), + precise, + } => (descriptor, calling_convention, precise), CallHandling::ApproxAsyncFn => { // Register a synthetic assignment of `future = (arg0, arg1, ...)`. let rvalue = Rvalue::Aggregate( @@ -378,23 +380,26 @@ impl<'tcx> PartialGraph<'tcx> { let is_root = |n: CallString| n.len() == 2; + let translator = PlaceTranslator::new( + constructor.async_info(), + constructor.def_id, + &constructor.mono_body, + constructor.tcx(), + *destination, + &calling_convention, + precise, + ); + // For each source node CHILD that is parentable to PLACE, // add an edge from PLACE -> CHILD. trace!("PARENT -> CHILD EDGES:"); for (child_src, _kind) in child_graph.parentable_srcs(is_root) { - if let Some(parent_place) = calling_convention.translate_to_parent( - child_src.place, - constructor.async_info(), - constructor.tcx(), - &constructor.mono_body, - constructor.def_id, - *destination, - ) { + if let Some(translation) = translator.translate_to_parent(child_src.place) { self.register_mutation( results, state, Inputs::Unresolved { - places: vec![(parent_place, None)], + places: vec![(translation, None)], }, Either::Right(child_src), location, @@ -410,14 +415,7 @@ impl<'tcx> PartialGraph<'tcx> { // the *last* nodes in the child function to the parent, not *all* of them. trace!("CHILD -> PARENT EDGES:"); for (child_dst, kind) in child_graph.parentable_dsts(is_root) { - if let Some(parent_place) = calling_convention.translate_to_parent( - child_dst.place, - constructor.async_info(), - constructor.tcx(), - &constructor.mono_body, - constructor.def_id, - *destination, - ) { + if let Some(parent_place) = translator.translate_to_parent(child_dst.place) { self.register_mutation( results, state, diff --git a/crates/flowistry_pdg_construction/src/local_analysis.rs b/crates/flowistry_pdg_construction/src/local_analysis.rs index 3113f3b843..010c7a2595 100644 --- a/crates/flowistry_pdg_construction/src/local_analysis.rs +++ b/crates/flowistry_pdg_construction/src/local_analysis.rs @@ -14,7 +14,7 @@ use rustc_middle::{ visit::Visitor, AggregateKind, BasicBlock, Body, HasLocalDecls, Location, Operand, Place, PlaceElem, Rvalue, Statement, Terminator, TerminatorEdges, TerminatorKind, RETURN_PLACE, }, - ty::{GenericArgKind, GenericArgsRef, Instance, TyCtxt, TyKind}, + ty::{GenericArgKind, GenericArgsRef, Instance, InstanceDef, TyCtxt, TyKind}, }; use rustc_mir_dataflow::{self as df, fmt::DebugWithContext, Analysis}; use rustc_span::Span; @@ -27,7 +27,7 @@ use crate::{ calling_convention::*, graph::{DepEdge, DepNode, PartialGraph, SourceUse, TargetUse}, mutation::{ModularMutationVisitor, Mutation, Time}, - utils::{self, is_async, is_virtual, try_monomorphize}, + utils::{self, is_async, is_virtual, try_monomorphize, type_as_fn}, CallChangeCallback, CallChanges, CallInfo, InlineMissReason, MemoPdgConstructor, SkipCall, }; @@ -220,6 +220,7 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { state: &InstructionState<'tcx>, input: Place<'tcx>, ) -> Vec> { + trace!("Finding inputs for place {input:?}"); // Include all sources of indirection (each reference in the chain) as relevant places. let provenance = input .refs_in_projection(&self.mono_body, self.tcx()) @@ -261,6 +262,7 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { place = new_place; } } + trace!("Checking conflict status of {place:?} and {alias:?}"); places_conflict( self.tcx(), &self.mono_body, @@ -411,6 +413,27 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { } return None; }; + + let call_kind = if matches!(resolved_fn.def, InstanceDef::ClosureOnceShim { .. }) { + // Rustc has inserted a call to the shim that maps `Fn` and `FnMut` + // instances to an `FnOnce`. This shim has no body itself so we + // can't just inline, we must explicitly simulate it's effects by + // changing the target function and by setting the calling + // convention to that of a shim. + + // Because this is a well defined internal item we can make + // assumptions about its generic arguments. + let Some((func_a, _rest)) = generic_args.split_first() else { + unreachable!() + }; + let Some((func_t, g)) = type_as_fn(self.tcx(), func_a.expect_ty()) else { + unreachable!() + }; + resolved_fn = Instance::expect_resolve(tcx, param_env, func_t, g); + CallKind::Indirect { once_shim: true } + } else { + self.classify_call_kind(called_def_id, resolved_fn, &args, span) + }; let resolved_def_id = resolved_fn.def_id(); if log_enabled!(Level::Trace) && called_def_id != resolved_def_id { let (called, resolved) = (self.fmt_fn(called_def_id), self.fmt_fn(resolved_def_id)); @@ -421,13 +444,16 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { return Some(CallHandling::ApproxAsyncSM(handler)); }; - let call_kind = self.classify_call_kind(called_def_id, resolved_fn, &args, span); - trace!( " Handling call! with kind {}", match &call_kind { CallKind::Direct => "direct", - CallKind::Indirect => "indirect", + CallKind::Indirect { once_shim } => + if *once_shim { + "indirect (once shim)" + } else { + "indirect" + }, CallKind::AsyncPoll { .. } => "async poll", } ); @@ -480,7 +506,7 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { .then_some(CallHandling::ApproxAsyncFn); } - let calling_convention = match call_changes { + let (calling_convention, precise) = match call_changes { Some(CallChanges { skip: SkipCall::Skip, }) => { @@ -496,9 +522,9 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { }) => { trace!(" Replacing call as instructed by user"); resolved_fn = instance; - calling_convention + (calling_convention, false) } - _ => CallingConvention::from_call_kind(&call_kind, args), + _ => (CallingConvention::from_call_kind(&call_kind, args), true), }; if is_virtual(tcx, resolved_def_id) { trace!(" bailing because is unresolvable trait method"); @@ -522,6 +548,7 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { Some(CallHandling::Ready { descriptor, calling_convention, + precise, }) } @@ -548,11 +575,12 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { trace!("Call handling is {}", preamble.as_ref()); - let (child_constructor, calling_convention) = match preamble { + let (child_constructor, calling_convention, precise) = match preamble { CallHandling::Ready { descriptor, calling_convention, - } => (descriptor, calling_convention), + precise, + } => (descriptor, calling_convention, precise), CallHandling::ApproxAsyncFn => { // Register a synthetic assignment of `future = (arg0, arg1, ...)`. let rvalue = Rvalue::Aggregate( @@ -578,6 +606,16 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { let parentable_dsts = child_constructor.parentable_dsts(|n| n.len() == 1); let parent_body = &self.mono_body; + let place_translator = PlaceTranslator::new( + self.async_info(), + self.def_id, + parent_body, + self.tcx(), + destination, + &calling_convention, + precise, + ); + // For each destination node CHILD that is parentable to PLACE, // add an edge from CHILD -> PLACE. // @@ -585,14 +623,7 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { // the *last* nodes in the child function to the parent, not *all* of them. trace!("CHILD -> PARENT EDGES:"); for (child_dst, _) in parentable_dsts { - if let Some(parent_place) = calling_convention.translate_to_parent( - child_dst.place, - self.async_info(), - self.tcx(), - parent_body, - self.def_id, - destination, - ) { + if let Some(parent_place) = place_translator.translate_to_parent(child_dst.place) { self.apply_mutation(state, location, parent_place); } } @@ -687,7 +718,9 @@ impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> { } fn try_indirect_call_kind(&self, def_id: DefId) -> Option> { - self.tcx().is_closure(def_id).then_some(CallKind::Indirect) + self.tcx() + .is_closure(def_id) + .then_some(CallKind::Indirect { once_shim: false }) } fn terminator_visitor<'b: 'a>( @@ -779,7 +812,11 @@ pub enum CallKind<'tcx> { /// A standard function call like `f(x)`. Direct, /// A call to a function variable, like `fn foo(f: impl Fn()) { f() }` - Indirect, + Indirect { + /// The call takes place via a shim that implements `FnOnce` for a `Fn` + /// or `FnMut` closure. + once_shim: bool, + }, /// A poll to an async function, like `f.await`. AsyncPoll(AsyncFnPollEnv<'tcx>), } @@ -790,6 +827,7 @@ pub(crate) enum CallHandling<'tcx, 'a> { Ready { calling_convention: CallingConvention<'tcx>, descriptor: &'a PartialGraph<'tcx>, + precise: bool, }, ApproxAsyncSM(ApproximationHandler<'tcx, 'a>), } diff --git a/crates/paralegal-flow/src/ana/graph_converter.rs b/crates/paralegal-flow/src/ana/graph_converter.rs index 4ef7818f9a..059c80c2f6 100644 --- a/crates/paralegal-flow/src/ana/graph_converter.rs +++ b/crates/paralegal-flow/src/ana/graph_converter.rs @@ -100,6 +100,7 @@ impl<'a, 'tcx, C: Extend> GraphConverter<'tcx, 'a, C> { generator.tcx, def_id, generator.pdg_constructor.body_cache(), + generator.marker_ctx().clone(), ), stats, }) @@ -599,12 +600,12 @@ mod call_string_resolver { utils::{manufacture_substs_for, try_monomorphize, try_resolve_function}, }; use paralegal_spdg::Endpoint; - use rustc_middle::ty::Instance; + use rustc_middle::{mir::TerminatorKind, ty::Instance}; use rustc_utils::cache::Cache; - use crate::{Either, TyCtxt}; + use crate::{Either, MarkerCtx, TyCtxt}; - use super::{map_either, match_async_trait_assign, AsFnAndArgs}; + use super::{func_of_term, map_either, match_async_trait_assign, AsFnAndArgs}; /// Cached resolution of [`CallString`]s to [`FnResolution`]s. /// @@ -615,6 +616,7 @@ mod call_string_resolver { tcx: TyCtxt<'tcx>, entrypoint_is_async: bool, body_cache: &'a BodyCache<'tcx>, + marker_context: MarkerCtx<'tcx>, } impl<'tcx, 'a> CallStringResolver<'tcx, 'a> { @@ -645,12 +647,14 @@ mod call_string_resolver { tcx: TyCtxt<'tcx>, entrypoint: Endpoint, body_cache: &'a BodyCache<'tcx>, + marker_context: MarkerCtx<'tcx>, ) -> Self { Self { cache: Default::default(), tcx, entrypoint_is_async: super::entrypoint_is_async(body_cache, tcx, entrypoint), body_cache, + marker_context, } } @@ -681,7 +685,21 @@ mod call_string_resolver { }, ); let res = match normalized { - Either::Right(term) => term.as_instance_and_args(tcx).unwrap().0, + Either::Right(term) => { + let (def_id, args) = func_of_term(tcx, &term).unwrap(); + let instance = Instance::expect_resolve(tcx, param_env, def_id, args); + if let Some(model) = self.marker_context.has_stub(def_id) { + let TerminatorKind::Call { args, .. } = &term.kind else { + unreachable!() + }; + model + .apply(tcx, instance, param_env, args, term.source_info.span) + .unwrap() + .0 + } else { + term.as_instance_and_args(tcx).unwrap().0 + } + } Either::Left(stmt) => { let (def_id, generics) = match_async_trait_assign(&stmt).unwrap(); try_resolve_function(tcx, def_id, param_env, generics).unwrap() diff --git a/crates/paralegal-flow/src/ana/inline_judge.rs b/crates/paralegal-flow/src/ana/inline_judge.rs index 198dc1066e..a6d8bac233 100644 --- a/crates/paralegal-flow/src/ana/inline_judge.rs +++ b/crates/paralegal-flow/src/ana/inline_judge.rs @@ -14,7 +14,7 @@ use rustc_type_ir::TyKind; use crate::{ ana::Print, ann::db::MarkerDatabase, - args::{FlowModel, InliningDepth}, + args::{InliningDepth, Stub}, Args, MarkerCtx, TyCtxt, }; @@ -36,20 +36,10 @@ pub struct InlineJudge<'tcx> { pub enum InlineJudgement { /// Construct a graph for the called function and merge it Inline, - /// Use a flow model to abstract the call - UseFlowModel(&'static FlowModel), + /// Use a stub instead of the call + UseStub(&'static Stub), /// Abstract the call via type signature - AbstractViaType, -} - -impl From for InlineJudgement { - fn from(value: bool) -> Self { - if value { - InlineJudgement::Inline - } else { - InlineJudgement::AbstractViaType - } - } + AbstractViaType(&'static str), } impl<'tcx> InlineJudge<'tcx> { @@ -89,24 +79,44 @@ impl<'tcx> InlineJudge<'tcx> { pub fn should_inline(&self, info: &CallInfo<'tcx, '_>) -> InlineJudgement { let marker_target = info.async_parent.unwrap_or(info.callee); let marker_target_def_id = marker_target.def_id(); - if let Some(model) = self.marker_ctx().has_flow_model(marker_target_def_id) { - return InlineJudgement::UseFlowModel(model); + if let Some(model) = self.marker_ctx().has_stub(marker_target_def_id) { + // If we're replacing an async function skip the poll call. + // + // I tried to have it replace the poll call only but that didn't seem to work. + return if info.async_parent.is_some() { + InlineJudgement::AbstractViaType("async parent of stub") + } else { + InlineJudgement::UseStub(model) + }; } let is_marked = self.marker_ctx.is_marked(marker_target_def_id); let judgement = match self.opts.anactrl().inlining_depth() { - _ if !self.included_crates.contains(&marker_target_def_id.krate) || is_marked => { - InlineJudgement::AbstractViaType + _ if !self.included_crates.contains(&marker_target_def_id.krate) => { + InlineJudgement::AbstractViaType("inlining for crate disabled") + } + _ if is_marked => InlineJudgement::AbstractViaType("marked"), + InliningDepth::Adaptive + if self + .marker_ctx + .has_transitive_reachable_markers(marker_target) => + { + InlineJudgement::Inline + } + InliningDepth::Adaptive => InlineJudgement::AbstractViaType("adaptive inlining"), + InliningDepth::Shallow => { + InlineJudgement::AbstractViaType("shallow inlining configured") } - InliningDepth::Adaptive => self - .marker_ctx - .has_transitive_reachable_markers(marker_target) - .into(), - InliningDepth::Shallow => InlineJudgement::AbstractViaType, InliningDepth::Unconstrained => InlineJudgement::Inline, }; - if matches!(judgement, InlineJudgement::AbstractViaType) { + if let InlineJudgement::AbstractViaType(reason) = judgement { let emit_err = !(is_marked || self.opts.relaxed()); - self.ensure_is_safe_to_approximate(info.param_env, info.callee, info.span, emit_err) + self.ensure_is_safe_to_approximate( + info.param_env, + info.callee, + info.span, + emit_err, + reason, + ) } judgement } @@ -121,6 +131,7 @@ impl<'tcx> InlineJudge<'tcx> { resolved: Instance<'tcx>, call_span: Span, emit_err: bool, + reason: &'static str, ) { SafetyChecker { tcx: self.tcx(), @@ -129,6 +140,7 @@ impl<'tcx> InlineJudge<'tcx> { resolved, call_span, marker_ctx: self.marker_ctx.clone(), + reason, } .check() } @@ -151,13 +163,18 @@ struct SafetyChecker<'tcx> { resolved: Instance<'tcx>, call_span: Span, marker_ctx: MarkerCtx<'tcx>, + /// Why a call we check wasn't inlined + reason: &'static str, } impl<'tcx> SafetyChecker<'tcx> { /// Emit an error or a warning with some preformatted messaging. fn err(&self, s: &str, span: Span) { let sess = self.tcx.sess; - let msg = format!("Cannot verify that non-inlined function is safe due to: {s}"); + let msg = format!( + "the call is not safe to abstract as demanded by '{}', because of: {s}", + self.reason + ); if self.emit_err { let mut diagnostic = sess.struct_span_err(span, msg); diagnostic.span_note(self.call_span, "Called from here"); diff --git a/crates/paralegal-flow/src/ana/mod.rs b/crates/paralegal-flow/src/ana/mod.rs index 791215fb6b..a34b7ed6c5 100644 --- a/crates/paralegal-flow/src/ana/mod.rs +++ b/crates/paralegal-flow/src/ana/mod.rs @@ -6,7 +6,7 @@ use crate::{ ann::{Annotation, MarkerAnnotation}, - args::FlowModel, + args::Stub, desc::*, discover::FnToAnalyze, stats::{Stats, TimedStat}, @@ -29,10 +29,10 @@ use petgraph::visit::GraphBase; use rustc_hir::{self as hir, def, def_id::DefId}; use rustc_middle::{ - mir::Location, + mir::{Location, Operand}, ty::{Instance, ParamEnv, TyCtxt}, }; -use rustc_span::{FileNameDisplayPreference, Span as RustSpan, Symbol}; +use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Span as RustSpan, Symbol}; mod graph_converter; mod inline_judge; @@ -333,6 +333,7 @@ fn src_loc_for_span(span: RustSpan, tcx: TyCtxt) -> Span { fn default_index() -> ::NodeId { ::NodeId::end() } + /// Checks the invariant that [`SPDGGenerator::collect_type_info`] should /// produce a map that is a superset of the types found in all the `types` maps /// on [`SPDG`]. @@ -348,6 +349,7 @@ fn type_info_sanity_check(controllers: &ControllerMap, types: &TypeInfoMap) { ); }) } + fn def_kind_for_item(id: DefId, tcx: TyCtxt) -> DefKind { match tcx.def_kind(id) { def::DefKind::Closure => DefKind::Closure, @@ -412,41 +414,136 @@ struct MyCallback<'tcx> { tcx: TyCtxt<'tcx>, } +impl Stub { + pub fn resolve_alternate_instance<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + function: Instance<'tcx>, + param_env: ParamEnv<'tcx>, + at: RustSpan, + ) -> Result, ErrorGuaranteed> { + match self { + Stub::SubClosure { generic_name } | Stub::SubFuture { generic_name } => { + let name = Symbol::intern(generic_name); + let generics = tcx.generics_of(function.def_id()); + let Some(param_index) = (0..generics.count()).find(|&idx| { + let param = generics.param_at(idx, tcx); + param.name == name + }) else { + return Err(tcx.sess.span_err( + at, + format!("Function has no parameter named {generic_name}"), + )); + }; + let ty = function.args[param_index].expect_ty(); + let (def_id, args) = + flowistry_pdg_construction::utils::type_as_fn(tcx, ty).unwrap(); + Ok(Instance::resolve(tcx, param_env, def_id, args) + .unwrap() + .unwrap()) + } + } + } + + fn indirect_required( + &self, + tcx: TyCtxt, + def_id: DefId, + at: RustSpan, + ) -> Result { + let bool = match self { + Stub::SubClosure { .. } => { + use rustc_hir::def::DefKind; + match tcx.def_kind(def_id) { + DefKind::Closure => true, + DefKind::Fn => false, + kind => { + return Err(tcx.sess.span_err( + at, + format!("Expected `fn` or `closure` def kind, got {kind:?}"), + )) + } + } + } + Stub::SubFuture { .. } => { + assert!(tcx.generator_is_async(def_id)); + true + } + }; + Ok(bool) + } + + /// Performs the effects of this model on the provided function. + /// + /// `function` is what was to be called but for which a stub exists, + /// `arguments` are the arguments to that call. + /// + /// Returns a new instance to call instead and how it should be called. + pub fn apply<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + function: Instance<'tcx>, + param_env: ParamEnv<'tcx>, + arguments: &[Operand<'tcx>], + at: RustSpan, + ) -> Result<(Instance<'tcx>, CallingConvention<'tcx>), ErrorGuaranteed> { + let instance = self.resolve_alternate_instance(tcx, function, param_env, at)?; + let def_id = instance.def_id(); + + let expect_indirect = self.indirect_required(tcx, def_id, at)?; + let poll = tcx.lang_items().poll(); + let calling_convention = if expect_indirect { + let clj = match arguments { + [clj] => clj, + [gen, _] + if tcx.def_kind(function.def_id()) == hir::def::DefKind::AssocFn + && tcx.associated_item(function.def_id()).trait_item_def_id == poll => + { + gen + } + _ => { + return Err(tcx.sess.span_err( + at, + format!( + "this function ({:?}) should have only one argument but it has {}", + function.def_id(), + arguments.len() + ), + )) + } + }; + CallingConvention::Indirect { + once_shim: false, + closure_arg: clj.clone(), + // This is incorrect, but we only support + // non-argument closures at the moment so this + // will never be used. + tupled_arguments: clj.clone(), + } + } else { + CallingConvention::Direct(arguments.into()) + }; + Ok((instance, calling_convention)) + } +} + impl<'tcx> CallChangeCallback<'tcx> for MyCallback<'tcx> { fn on_inline(&self, info: CallInfo<'tcx, '_>) -> CallChanges<'tcx> { let changes = CallChanges::default(); let skip = match self.judge.should_inline(&info) { - InlineJudgement::AbstractViaType => SkipCall::Skip, - InlineJudgement::UseFlowModel(model) => { - // Set in case of errors - assert!(matches!( - model, - FlowModel::SubClosure | FlowModel::SubFuture - )); - if let [clj] = &info.arguments { - let ty = clj.ty(info.caller_body, self.tcx); - let (def_id, args) = - flowistry_pdg_construction::utils::type_as_fn(self.tcx, ty).unwrap(); - let instance = Instance::resolve(self.tcx, info.param_env, def_id, args) - .unwrap() - .unwrap(); - assert_eq!(instance.sig(self.tcx).unwrap().inputs().len(), 1); - match model { - FlowModel::SubClosure => { - assert_eq!(self.tcx.def_kind(def_id), rustc_hir::def::DefKind::Closure) - } - FlowModel::SubFuture => assert!(self.tcx.generator_is_async(def_id)), - }; + InlineJudgement::AbstractViaType(_) => SkipCall::Skip, + InlineJudgement::UseStub(model) => { + if let Ok((instance, calling_convention)) = model.apply( + self.tcx, + info.callee, + info.param_env, + info.arguments, + info.span, + ) { SkipCall::Replace { instance, - calling_convention: CallingConvention::Indirect { - closure_arg: clj.clone(), - // This is incorrect, but we only support - // non-argument closures at the moment so this - // will never be used. - tupled_arguments: clj.clone(), - }, + calling_convention, } } else { SkipCall::Skip @@ -462,11 +559,19 @@ impl<'tcx> CallChangeCallback<'tcx> for MyCallback<'tcx> { param_env: ParamEnv<'tcx>, _loc: Location, _parent: Instance<'tcx>, - _reason: InlineMissReason, + reason: InlineMissReason, call_span: rustc_span::Span, ) { - self.judge - .ensure_is_safe_to_approximate(param_env, resolution, call_span, false); + self.judge.ensure_is_safe_to_approximate( + param_env, + resolution, + call_span, + false, + match reason { + InlineMissReason::Async(_) => "async", + InlineMissReason::TraitMethod => "virtual trait method", + }, + ); } } diff --git a/crates/paralegal-flow/src/ann/db.rs b/crates/paralegal-flow/src/ann/db.rs index 19a041843b..440f7aff70 100644 --- a/crates/paralegal-flow/src/ann/db.rs +++ b/crates/paralegal-flow/src/ann/db.rs @@ -12,9 +12,9 @@ use crate::{ ann::{Annotation, MarkerAnnotation}, - args::{Args, FlowModel}, + args::{Args, Stub}, utils::{ - resolve::expect_resolve_string_to_def_id, ty_of_const, FunctionKind, InstanceExt, + func_of_term, resolve::expect_resolve_string_to_def_id, FunctionKind, InstanceExt, IntoDefId, TyExt, }, Either, HashMap, HashSet, @@ -24,10 +24,11 @@ use flowistry_pdg_construction::{ body_cache::{local_or_remote_paths, BodyCache}, determine_async, encoder::ParalegalDecoder, - utils::{is_virtual, try_monomorphize, try_resolve_function, type_as_fn}, + utils::{is_virtual, try_monomorphize, try_resolve_function}, }; use paralegal_spdg::Identifier; +use rustc_errors::DiagnosticMessage; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hir::def_id::DefId; use rustc_hir::{def::DefKind, def_id::CrateNum}; @@ -37,6 +38,7 @@ use rustc_middle::{ }; use rustc_serialize::Decodable; +use rustc_span::Span; use rustc_utils::cache::Cache; use std::{borrow::Cow, fs::File, io::Read, rc::Rc}; @@ -205,26 +207,16 @@ impl<'tcx> MarkerCtx<'tcx> { res: impl Into>, ) -> impl Iterator + '_ { let res = res.into(); - // TODO this check is wrong. This should either check whether this gets inlined or be removed altogether. - if res.def_id().is_local() { - let mut direct_markers = self - .combined_markers(res.def_id()) - .map(|m| m.marker) - .peekable(); - let non_direct = direct_markers - .peek() - .is_none() - .then(|| self.get_reachable_markers(res)); - - Either::Right(direct_markers.chain(non_direct.into_iter().flatten().copied())) - } else { - Either::Left( - self.all_function_markers(res) - .map(|m| m.0.marker) - .collect::>(), - ) - } - .into_iter() + let mut direct_markers = self + .combined_markers(res.def_id()) + .map(|m| m.marker) + .peekable(); + let non_direct = direct_markers + .peek() + .is_none() + .then(|| self.get_reachable_markers(res)); + + direct_markers.chain(non_direct.into_iter().flatten().copied()) } /// If the transitive marker cache did not contain the answer, this is what @@ -264,6 +256,14 @@ impl<'tcx> MarkerCtx<'tcx> { .collect() } + fn span_err(&self, span: Span, msg: impl Into) { + if self.0.config.relaxed() { + self.tcx().sess.span_warn(span, msg.into()); + } else { + self.tcx().sess.span_err(span, msg.into()); + } + } + /// Does this terminator carry a marker? fn terminator_reachable_markers( &self, @@ -271,36 +271,22 @@ impl<'tcx> MarkerCtx<'tcx> { terminator: &mir::Terminator<'tcx>, expect_resolve: bool, ) -> impl Iterator + '_ { + let param_env = ty::ParamEnv::reveal_all(); let mut v = vec![]; trace!( " Finding reachable markers for terminator {:?}", terminator.kind ); - let mir::TerminatorKind::Call { func, .. } = &terminator.kind else { - return v.into_iter(); - }; - let Some(const_) = func.constant() else { - return v.into_iter(); - }; - let ty = ty_of_const(const_); - let Some((def_id, gargs)) = type_as_fn(self.tcx(), ty) else { + let Some((def_id, gargs)) = func_of_term(self.tcx(), terminator) else { return v.into_iter(); }; - let res = if expect_resolve { - let Some(instance) = - Instance::resolve(self.tcx(), ty::ParamEnv::reveal_all(), def_id, gargs).unwrap() + let mut res = if expect_resolve { + let Some(instance) = Instance::resolve(self.tcx(), param_env, def_id, gargs).unwrap() else { - if self.0.config.relaxed() { - self.tcx().sess.span_warn( - terminator.source_info.span, - format!("cannot determine reachable markers, failed to resolve {def_id:?} with {gargs:?}") - ); - } else { - self.tcx().sess.span_err( + self.span_err( terminator.source_info.span, format!("cannot determine reachable markers, failed to resolve {def_id:?} with {gargs:?}") ); - } return v.into_iter(); }; MaybeMonomorphized::Monomorphized(instance) @@ -311,6 +297,26 @@ impl<'tcx> MarkerCtx<'tcx> { " Checking function {} for markers", self.tcx().def_path_debug_str(res.def_id()) ); + + if let Some(model) = self.has_stub(res.def_id()) { + if let MaybeMonomorphized::Monomorphized(instance) = &mut res { + if let Ok(new_instance) = model.resolve_alternate_instance( + self.tcx(), + *instance, + param_env, + terminator.source_info.span, + ) { + v.extend(self.get_reachable_and_self_markers(new_instance)); + } + } else { + self.span_err( + terminator.source_info.span, + "Could not apply stub to an partially resolved function", + ); + }; + return v.into_iter(); + } + v.extend(self.get_reachable_and_self_markers(res)); // We have to proceed differently than graph construction, @@ -501,7 +507,7 @@ impl<'tcx> MarkerCtx<'tcx> { cache.keys().copied().collect::>() } - pub fn has_flow_model(&self, def_id: DefId) -> Option<&'static FlowModel> { + pub fn has_stub(&self, def_id: DefId) -> Option<&'static Stub> { [def_id] .into_iter() .chain( @@ -509,7 +515,7 @@ impl<'tcx> MarkerCtx<'tcx> { .then(|| self.tcx().associated_item(def_id).trait_item_def_id) .flatten(), ) - .find_map(|def_id| self.0.flow_models.get(&def_id)) + .find_map(|def_id| self.0.stubs.get(&def_id)) .copied() } } @@ -573,7 +579,7 @@ pub struct MarkerDatabase<'tcx> { type_markers: Cache, Box>, body_cache: Rc>, included_crates: FxHashSet, - flow_models: FxHashMap, + stubs: FxHashMap, } impl<'tcx> MarkerDatabase<'tcx> { @@ -584,9 +590,9 @@ impl<'tcx> MarkerDatabase<'tcx> { body_cache: Rc>, included_crates: FxHashSet, ) -> Self { - let flow_models = args + let stubs = args .build_config() - .flow_models + .stubs .iter() .filter_map(|(k, v)| { let res = expect_resolve_string_to_def_id(tcx, k, args.relaxed()); @@ -603,7 +609,7 @@ impl<'tcx> MarkerDatabase<'tcx> { type_markers: Default::default(), body_cache, included_crates, - flow_models, + stubs, } } } diff --git a/crates/paralegal-flow/src/args.rs b/crates/paralegal-flow/src/args.rs index fbb3fa841f..93d48df7e1 100644 --- a/crates/paralegal-flow/src/args.rs +++ b/crates/paralegal-flow/src/args.rs @@ -13,7 +13,8 @@ use anyhow::Error; use clap::ValueEnum; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; -use std::path::PathBuf; +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; use crate::utils::TinyBitSet; use crate::{num_derive, num_traits::FromPrimitive}; @@ -73,12 +74,16 @@ impl TryFrom for Args { .extend(from_env.split(',').map(ToOwned::to_owned)); } let build_config_file = std::path::Path::new("Paralegal.toml"); - let build_config: BuildConfig = if build_config_file.exists() { - toml::from_str(&std::fs::read_to_string(build_config_file)?)? + let build_config: (_, BuildConfig) = if let Ok(absolute) = build_config_file.canonicalize() + { + let config = toml::from_str(&std::fs::read_to_string(&absolute)?)?; + (Some(absolute), config) } else { Default::default() }; - anactrl.include.extend(build_config.include.iter().cloned()); + anactrl + .include + .extend(build_config.1.include.iter().cloned()); let log_level_config = match debug_target { Some(target) if !target.is_empty() => LogLevelConfig::Targeted(target), _ => LogLevelConfig::Disabled, @@ -137,7 +142,7 @@ pub struct Args { /// Additional arguments that control debug output specifically dump: DumpArgs, /// Additional configuration for the build process/rustc - build_config: BuildConfig, + build_config: (Option, BuildConfig), /// Additional options for cargo cargo_args: Vec, } @@ -362,7 +367,20 @@ impl Args { self.abort_after_analysis } pub fn build_config(&self) -> &BuildConfig { - &self.build_config + &self.build_config.1 + } + + pub fn hash_config(&self, hasher: &mut impl Hasher) { + if self.attach_to_debugger.is_some() { + // If we run the debugger try to make the hash fail so we actually run. + std::time::Instant::now().hash(hasher); + } + // TODO Add other relevant arguments + config_hash_for_file(&self.build_config.0, hasher); + self.relaxed.hash(hasher); + self.target.hash(hasher); + self.result_path.hash(hasher); + config_hash_for_file(&self.marker_control.external_annotations, hasher); } pub fn marker_control(&self) -> &MarkerControl { @@ -394,6 +412,17 @@ impl Args { } } +fn config_hash_for_file(path: &Option>, state: &mut impl Hasher) { + path.as_ref() + .map(|path| { + let path = path.as_ref(); + Ok::<_, std::io::Error>((path, path.metadata()?.modified()?)) + }) + .transpose() + .unwrap() + .hash(state); +} + #[derive(serde::Serialize, serde::Deserialize, clap::Args, Default)] pub struct MarkerControl { /// A JSON file from which to load additional annotations. Whereas normally @@ -552,12 +581,14 @@ pub struct DepConfig { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[serde(tag = "mode", rename_all = "kebab-case")] -pub enum FlowModel { +pub enum Stub { + #[serde(rename_all = "kebab-case")] /// Replaces the result of a call to a higher-order function with a call to /// the input closure. - SubClosure, + SubClosure { generic_name: String }, + #[serde(rename_all = "kebab-case")] /// Replaces the result of a higher-order future by an input future. - SubFuture, + SubFuture { generic_name: String }, } /// Additional configuration for the build process/rustc @@ -570,5 +601,5 @@ pub struct BuildConfig { #[serde(default)] pub include: Vec, #[serde(default)] - pub flow_models: HashMap, + pub stubs: HashMap, } diff --git a/crates/paralegal-flow/src/lib.rs b/crates/paralegal-flow/src/lib.rs index 9b017d9607..498c238dc8 100644 --- a/crates/paralegal-flow/src/lib.rs +++ b/crates/paralegal-flow/src/lib.rs @@ -29,6 +29,7 @@ extern crate rustc_ast; extern crate rustc_borrowck; extern crate rustc_data_structures; extern crate rustc_driver; +extern crate rustc_errors; extern crate rustc_hash; extern crate rustc_hir; extern crate rustc_index; @@ -298,6 +299,10 @@ impl rustc_plugin::RustcPlugin for DfppPlugin { env!("RUSTC_VERSION").into() } + fn hash_config(&self, args: &Self::Args, hasher: &mut impl std::hash::Hasher) { + args.hash_config(hasher); + } + fn args( &self, _target_dir: &rustc_plugin::Utf8Path, diff --git a/crates/paralegal-flow/src/utils/mod.rs b/crates/paralegal-flow/src/utils/mod.rs index 99781edd8b..9e41a7d410 100644 --- a/crates/paralegal-flow/src/utils/mod.rs +++ b/crates/paralegal-flow/src/utils/mod.rs @@ -19,8 +19,8 @@ use rustc_hir::{ BodyId, }; use rustc_middle::{ - mir::{self, Constant, Location, Place, ProjectionElem}, - ty::{self, Instance, Ty}, + mir::{self, Constant, Location, Place, ProjectionElem, Terminator}, + ty::{self, GenericArgsRef, Instance, Ty}, }; use rustc_span::{symbol::Ident, Span as RustSpan, Span}; use rustc_target::spec::abi::Abi; @@ -275,6 +275,18 @@ impl FunctionKind { } } +pub fn func_of_term<'tcx>( + tcx: TyCtxt<'tcx>, + terminator: &Terminator<'tcx>, +) -> Option<(DefId, GenericArgsRef<'tcx>)> { + let mir::TerminatorKind::Call { func, .. } = &terminator.kind else { + return None; + }; + let const_ = func.constant()?; + let ty = ty_of_const(const_); + type_as_fn(tcx, ty) +} + /// A simplified version of the argument list that is stored in a /// `TerminatorKind::Call`. /// diff --git a/crates/paralegal-flow/src/utils/resolve.rs b/crates/paralegal-flow/src/utils/resolve.rs index 5b15f46fb8..39fd797944 100644 --- a/crates/paralegal-flow/src/utils/resolve.rs +++ b/crates/paralegal-flow/src/utils/resolve.rs @@ -161,7 +161,7 @@ fn non_local_item_children_by_name( .iter() .find(|item| item.ident.name == name) .map(|child| Res::from_def_res(child.res.expect_non_local())), - DefKind::Impl { of_trait: false } => tcx + DefKind::Impl { .. } => tcx .associated_item_def_ids(def_id) .iter() .copied() diff --git a/crates/paralegal-flow/tests/flow-models/Cargo.lock b/crates/paralegal-flow/tests/flow-models/Cargo.lock deleted file mode 100644 index 086f75321b..0000000000 --- a/crates/paralegal-flow/tests/flow-models/Cargo.lock +++ /dev/null @@ -1,144 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "backtrace" -version = "0.3.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "cc" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "flow-model-tests" -version = "0.1.0" -dependencies = [ - "paralegal", - "tokio", -] - -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "object" -version = "0.36.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" -dependencies = [ - "memchr", -] - -[[package]] -name = "paralegal" -version = "0.1.0" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "tokio" -version = "1.39.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" -dependencies = [ - "backtrace", - "pin-project-lite", -] - -[[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/flow-models/Paralegal.toml b/crates/paralegal-flow/tests/flow-models/Paralegal.toml deleted file mode 100644 index d6243d936e..0000000000 --- a/crates/paralegal-flow/tests/flow-models/Paralegal.toml +++ /dev/null @@ -1,6 +0,0 @@ -[flow-models."std::thread::spawn"] -mode = "sub-closure" - - -[flow-models."tokio::spawn"] -mode = "sub-future" diff --git a/crates/paralegal-flow/tests/flow-models/src/main.rs b/crates/paralegal-flow/tests/flow-models/src/main.rs deleted file mode 100644 index 9f1e6e25f9..0000000000 --- a/crates/paralegal-flow/tests/flow-models/src/main.rs +++ /dev/null @@ -1,28 +0,0 @@ -#[paralegal::marker(source, return)] -fn source() -> usize { - 0 -} - -#[paralegal::marker(pass, arguments = [0])] -fn pass(t: T) -> T { - t -} - -#[paralegal::marker(target, arguments = [0])] -fn target(i: usize) {} - -#[paralegal::analyze] -fn thread_spawn() { - let src = source(); - let next = std::thread::spawn(move || pass(src)).join().unwrap(); - target(next); -} - -fn main() {} - -#[paralegal::analyze] -async fn async_spawn() { - let src = source(); - let next = tokio::spawn(async move { pass(src) }).await.unwrap(); - target(next); -} diff --git a/crates/paralegal-flow/tests/flow-models.rs b/crates/paralegal-flow/tests/stub-tests.rs similarity index 57% rename from crates/paralegal-flow/tests/flow-models.rs rename to crates/paralegal-flow/tests/stub-tests.rs index 4767005990..ab5577a225 100644 --- a/crates/paralegal-flow/tests/flow-models.rs +++ b/crates/paralegal-flow/tests/stub-tests.rs @@ -11,7 +11,7 @@ extern crate lazy_static; use paralegal_flow::{define_flow_test_template, test_utils::*}; use paralegal_spdg::Identifier; -const TEST_CRATE_NAME: &str = "tests/flow-models"; +const TEST_CRATE_NAME: &str = "tests/stub-tests"; lazy_static! { static ref TEST_CRATE_ANALYZED: bool = run_paralegal_flow_with_flow_graph_dump(TEST_CRATE_NAME); @@ -48,3 +48,44 @@ define_test!(async_spawn: graph -> { assert!(src.flows_to_data(&pass)); assert!(pass.flows_to_data(&target)); }); + +fn simple_source_target_flow(graph: CtrlRef<'_>) { + let src = graph.marked(Identifier::new_intern("source")); + let target = graph.marked(Identifier::new_intern("target")); + + assert!(!src.is_empty()); + assert!(!target.is_empty()); + + assert!(src.flows_to_data(&target)); +} + +define_test!(block_fn: graph -> { + simple_source_target_flow(graph) +}); + +define_test!(test_blocking_with_let_bound_closure: graph -> { + simple_source_target_flow(graph) +}); + +define_test!(block_closure: graph -> { + simple_source_target_flow(graph) +}); + +define_test!(strategic_overtaint: graph -> { + simple_source_target_flow(graph) +}); + +define_test!(strategic_overtaint_2: graph -> { + simple_source_target_flow(graph) +}); + +define_test!(no_taint_without_connection: graph -> { + + let src = graph.marked(Identifier::new_intern("source")); + let target = graph.marked(Identifier::new_intern("target")); + + assert!(!src.is_empty()); + assert!(!target.is_empty()); + + assert!(!src.flows_to_data(&target)); +}); diff --git a/crates/paralegal-flow/tests/stub-tests/Cargo.lock b/crates/paralegal-flow/tests/stub-tests/Cargo.lock new file mode 100644 index 0000000000..88de3e83e3 --- /dev/null +++ b/crates/paralegal-flow/tests/stub-tests/Cargo.lock @@ -0,0 +1,1301 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide 0.7.4", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "bytestring" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "flate2" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.0", +] + +[[package]] +name = "flow-model-tests" +version = "0.1.0" +dependencies = [ + "actix-web", + "paralegal", + "tokio", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-more" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" + +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "object" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paralegal" +version = "0.1.0" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "windows-sys", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/crates/paralegal-flow/tests/flow-models/Cargo.toml b/crates/paralegal-flow/tests/stub-tests/Cargo.toml similarity index 84% rename from crates/paralegal-flow/tests/flow-models/Cargo.toml rename to crates/paralegal-flow/tests/stub-tests/Cargo.toml index f90d580f57..6f190b36c8 100644 --- a/crates/paralegal-flow/tests/flow-models/Cargo.toml +++ b/crates/paralegal-flow/tests/stub-tests/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "flow-model-tests" +name = "stub-tests" version = "0.1.0" edition = "2021" @@ -9,3 +9,4 @@ edition = "2021" #async-std = "1" paralegal = { path = "../../../paralegal" } tokio = { version = "1", features = ["rt"] } +actix-web = { version = "4" } diff --git a/crates/paralegal-flow/tests/stub-tests/Paralegal.toml b/crates/paralegal-flow/tests/stub-tests/Paralegal.toml new file mode 100644 index 0000000000..80391dce09 --- /dev/null +++ b/crates/paralegal-flow/tests/stub-tests/Paralegal.toml @@ -0,0 +1,15 @@ +[dep.serde] +rust-features = ["saturating_int_impl"] + + +[stubs."std::thread::spawn"] +mode = "sub-closure" +generic-name = "F" + +[stubs."tokio::spawn"] +mode = "sub-future" +generic-name = "F" + +[stubs."actix_web::web::block"] +mode = "sub-closure" +generic-name = "F" diff --git a/crates/paralegal-flow/tests/stub-tests/src/main.rs b/crates/paralegal-flow/tests/stub-tests/src/main.rs new file mode 100644 index 0000000000..edff823519 --- /dev/null +++ b/crates/paralegal-flow/tests/stub-tests/src/main.rs @@ -0,0 +1,103 @@ +#[paralegal::marker(source, return)] +fn source() -> usize { + 0 +} + +#[paralegal::marker(pass, arguments = [0])] +fn pass(t: T) -> T { + t +} + +#[paralegal::marker(target, arguments = [0])] +fn target(i: usize) {} + +#[allow(dead_code)] +#[paralegal::analyze] +fn thread_spawn() { + let src = source(); + let next = std::thread::spawn(move || pass(src)).join().unwrap(); + target(next); +} + +fn main() {} + +#[allow(dead_code)] +#[paralegal::analyze] +async fn async_spawn() { + let src = source(); + let next = tokio::spawn(async move { pass(src) }).await.unwrap(); + target(next); +} + +fn to_block() -> Result { + Ok(source()) +} + +#[allow(dead_code)] +#[paralegal::analyze] +async fn block_fn() -> Result<(), actix_web::error::BlockingError> { + Ok(target(actix_web::web::block(to_block).await?? + 1)) +} + +#[allow(dead_code)] +#[paralegal::analyze] +async fn block_closure(to_close_over: usize) -> Result<(), actix_web::error::BlockingError> { + Ok(target( + actix_web::web::block(move || Ok(source() + to_close_over)).await?? + 1, + )) +} + +pub async fn blocking(pool: &str, f: F) -> T +where + F: FnOnce(usize) -> T + 'static + Send, + T: 'static + Send, +{ + let pool = pool.parse().unwrap(); + let res = actix_web::web::block(move || (f)(pool)).await.unwrap(); + + res +} + +#[allow(dead_code)] +#[paralegal::analyze] +async fn test_blocking_with_let_bound_closure(to_close_over: &str) { + let from_scope = 10; + let the_closure = move |u| u + source() + from_scope; + target(blocking(to_close_over, the_closure).await); +} + +#[allow(dead_code)] +#[paralegal::analyze] +async fn strategic_overtaint(to_close_over: usize) -> Result<(), actix_web::error::BlockingError> { + Ok(target( + actix_web::web::block(move || Ok((source(), to_close_over))) + .await?? + .0, + )) +} + +#[allow(dead_code)] +#[paralegal::analyze] +async fn strategic_overtaint_2( + to_close_over: usize, +) -> Result<(), actix_web::error::BlockingError> { + Ok(target( + actix_web::web::block(move || Ok((source(), to_close_over))) + .await?? + .1, + )) +} + +#[allow(dead_code)] +#[paralegal::analyze] +async fn no_taint_without_connection( + to_close_over: usize, +) -> Result<(), actix_web::error::BlockingError> { + Ok(target( + actix_web::web::block(move || { + let _no_use = source(); + Ok(to_close_over) + }) + .await??, + )) +} diff --git a/crates/paralegal-policy/src/context.rs b/crates/paralegal-policy/src/context.rs index 18a286f9d6..d836c4beda 100644 --- a/crates/paralegal-policy/src/context.rs +++ b/crates/paralegal-policy/src/context.rs @@ -17,7 +17,7 @@ use paralegal_spdg::{ use anyhow::{anyhow, bail, Result}; use itertools::{Either, Itertools}; use petgraph::prelude::Bfs; -use petgraph::visit::{EdgeFiltered, EdgeRef, IntoNeighborsDirected, Topo, Walker}; +use petgraph::visit::{EdgeFiltered, EdgeRef, IntoNeighborsDirected, Reversed, Topo, Walker}; use petgraph::Direction::Outgoing; use petgraph::{Direction, Incoming}; @@ -923,7 +923,8 @@ impl NodeExt for GlobalNode { &ctx.desc.controllers[&self.controller_id()] }; let mut ancestors = HashMap::new(); - let fg = edge_selection.filter_graph(&g.graph); + let filtered = edge_selection.filter_graph(&g.graph); + let fg = Reversed(&filtered); let mut found = false; 'outer: for this in petgraph::visit::Bfs::new(&fg, self.local_node()).iter(&fg) { for next in fg.neighbors_directed(this, Outgoing) {