1use std::collections::{HashMap, HashSet};
13
14use rustc_hir::{Safety, def::DefKind, def_id::DefId};
15use rustc_middle::{
16 mir::{
17 BasicBlock, Local, LocalDecls, Operand, Place, ProjectionElem, Rvalue, StatementKind,
18 TerminatorKind,
19 },
20 ty::{self, AssocKind, TyCtxt, TyKind},
21};
22
23use crate::analysis::alias::{FieldOrigin, resolve_any_field_origin, resolve_self_field_origin};
24use crate::helpers::fn_info::is_externally_reachable;
25use crate::{
26 helpers::mir_scan::check_safety,
27 verify::def_use::{PlaceBaseKey, PlaceKey},
28};
29
30pub(super) use crate::helpers::mir_utils::{call_destination, operand_mir_place, operand_place};
32use crate::helpers::mir_utils::{blocks_reachable_after_call, rvalue_any_place_matching};
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
38pub(super) enum HazardKind {
39 SharedView,
40 UniqueView,
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44pub(super) enum AliasProducer {
45 View(HazardKind),
46 OwnershipTransfer,
47 ReadMemory,
48}
49
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51enum RawAccessKind {
52 Read,
53 Write,
54}
55
56#[derive(Clone, Debug)]
57struct LocalCallsite<'tcx> {
58 pub caller: DefId,
59 pub block: BasicBlock,
60 pub args: Vec<Operand<'tcx>>,
61 pub destination: Option<Local>,
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
65pub(super) enum HazardCheck {
66 Safe(String),
67 Violation(String),
68 Inconclusive,
69}
70
71pub(super) fn alias_producer(callee: DefId) -> Option<AliasProducer> {
74 if crate::verify::api_classify::is_from_raw_parts_mut(Some(callee)) {
75 return Some(AliasProducer::View(HazardKind::UniqueView));
76 }
77 if crate::verify::api_classify::is_vec_ownership_transfer(Some(callee)) {
78 return Some(AliasProducer::OwnershipTransfer);
79 }
80 if crate::verify::api_classify::is_from_raw_parts(Some(callee))
81 || crate::verify::api_classify::is_cstr_from_ptr(Some(callee))
82 {
83 return Some(AliasProducer::View(HazardKind::SharedView));
84 }
85 if crate::verify::api_classify::is_ownership_transfer(Some(callee)) {
86 return Some(AliasProducer::OwnershipTransfer);
87 }
88 if crate::verify::api_classify::is_ptr_read(Some(callee)) {
89 return Some(AliasProducer::ReadMemory);
90 }
91 None
92}
93
94pub(super) fn alias_proved_for_param_local(
97 tcx: TyCtxt<'_>,
98 caller: DefId,
99 local_index: usize,
100 kind: HazardKind,
101) -> HazardCheck {
102 let body = tcx.optimized_mir(caller);
103 let ty = body.local_decls[Local::from_usize(local_index)].ty;
104 match ty.kind() {
105 ty::Ref(_, _, ty::Mutability::Mut) => HazardCheck::Safe(
106 "returned view reinterprets a &mut param; no hidden raw-pointer conflict".into(),
107 ),
108 ty::Ref(_, _, ty::Mutability::Not) => {
109 if kind == HazardKind::UniqueView {
110 HazardCheck::Violation(
111 "shared reference origin cannot safely produce a unique mut view".into(),
112 )
113 } else {
114 HazardCheck::Safe(
115 "returned shared view tied to shared reference; no shared alias conflict"
116 .into(),
117 )
118 }
119 }
120 _ if !matches!(ty.kind(), ty::RawPtr(..))
121 && local_index >= 1
122 && local_index <= body.arg_count =>
123 {
124 HazardCheck::Safe(
125 "returned view derives from an owned parameter; no external alias risk".into(),
126 )
127 }
128 _ => HazardCheck::Inconclusive,
129 }
130}
131
132pub(super) fn alias_proved_for_param_local_from_origin(
133 tcx: TyCtxt<'_>,
134 caller: DefId,
135 origin: &PlaceKey,
136 kind: HazardKind,
137) -> HazardCheck {
138 let body = tcx.optimized_mir(caller);
139 let local = match origin.base {
140 PlaceBaseKey::Local(l) => l,
141 _ => return HazardCheck::Inconclusive,
142 };
143 if !origin.fields.is_empty() {
144 return HazardCheck::Inconclusive;
145 }
146 let ty = body.local_decls[Local::from_usize(local)].ty;
147 match ty.kind() {
148 ty::Ref(_, _, ty::Mutability::Mut) if kind == HazardKind::SharedView => {
149 HazardCheck::Safe("shared raw-ptr-deref view through &mut param".into())
150 }
151 ty::Ref(_, _, ty::Mutability::Mut) => HazardCheck::Inconclusive,
152 ty::Ref(_, _, ty::Mutability::Not) if kind == HazardKind::SharedView => {
153 HazardCheck::Safe("shared raw-ptr-deref view through shared reference".into())
154 }
155 ty::Ref(_, _, ty::Mutability::Not) => HazardCheck::Violation(
156 "shared reference origin cannot safely produce a unique mut view".into(),
157 ),
158 _ => HazardCheck::Inconclusive,
159 }
160}
161
162pub(super) fn is_origin_a_reference(tcx: TyCtxt<'_>, caller: DefId, origin: &PlaceKey) -> bool {
163 let body = tcx.optimized_mir(caller);
164 let PlaceBaseKey::Local(mut local) = origin.base else {
165 return false;
166 };
167 if let ty::Ref(..) = body.local_decls[Local::from_usize(local)].ty.kind() {
168 return true;
169 }
170 let (resolved, _) = crate::verify::vm::alias_tree::AliasTree::build(tcx, caller)
171 .resolve_local_to_root(Local::from_usize(local));
172 if resolved >= 1 && resolved <= body.arg_count {
173 local = resolved;
174 }
175 matches!(
176 body.local_decls[Local::from_usize(local)].ty.kind(),
177 ty::Ref(..)
178 )
179}
180
181pub(super) fn resolve_param_origin(
187 tcx: TyCtxt<'_>,
188 caller: DefId,
189 origin: &PlaceKey,
190) -> Option<usize> {
191 let body = tcx.optimized_mir(caller);
192 if let PlaceBaseKey::Local(local) = origin.base {
193 if local >= 1 && local <= body.arg_count {
194 return Some(local);
195 }
196 let (resolved, _fields) = crate::verify::vm::alias_tree::AliasTree::build(tcx, caller)
197 .resolve_local_to_root(Local::from_usize(local));
198 if resolved >= 1 && resolved <= body.arg_count {
199 return Some(resolved);
200 }
201 }
202 None
203}
204
205fn param_index_of_origin(tcx: TyCtxt<'_>, caller: DefId, origin: &PlaceKey) -> Option<usize> {
212 let PlaceBaseKey::Local(local) = origin.base else {
213 return None;
214 };
215 if !origin.fields.is_empty() {
216 return None;
217 }
218 let body = tcx.optimized_mir(caller);
219 if local == 0 || local > body.arg_count {
220 return None;
221 }
222 let ty = body.local_decls[Local::from_usize(local)].ty;
223 matches!(ty.kind(), TyKind::RawPtr(..)).then_some(local - 1)
224}
225
226pub(super) fn destination_flows_to_return(
229 tcx: TyCtxt<'_>,
230 caller: DefId,
231 destination: Option<Local>,
232) -> bool {
233 let Some(destination) = destination else {
234 return false;
235 };
236 if destination.as_usize() == 0 {
237 return true;
238 }
239 let body = tcx.optimized_mir(caller);
240 if body.local_decls[Local::from_usize(0)].ty == body.local_decls[destination].ty {
241 return true;
242 }
243 let mut aliases: HashMap<Local, PlaceKey> = HashMap::new();
244 aliases.insert(
245 destination,
246 PlaceKey {
247 base: PlaceBaseKey::Local(destination.as_usize()),
248 fields: Vec::new(),
249 },
250 );
251 for block in body.basic_blocks.iter() {
252 for statement in &block.statements {
253 let StatementKind::Assign(assign) = &statement.kind else {
254 continue;
255 };
256 let (target, rvalue) = assign.as_ref();
257 if target.local.as_usize() == 0 {
258 if rvalue_mentions_local(rvalue, destination, &aliases) {
259 return true;
260 }
261 }
262 if rvalue_mentions_local(rvalue, destination, &aliases) {
263 aliases.insert(target.local, aliases[&destination].clone());
264 }
265 }
266 }
267 false
268}
269
270pub(super) fn self_field_origin(
271 tcx: TyCtxt<'_>,
272 caller: DefId,
273 place: &PlaceKey,
274) -> Option<FieldOrigin> {
275 let PlaceBaseKey::Local(local) = place.base else {
276 return None;
277 };
278 resolve_self_field_origin(tcx, caller, local, &place.fields)
279}
280
281pub(super) fn any_struct_field_origin(
282 tcx: TyCtxt<'_>,
283 caller: DefId,
284 place: &PlaceKey,
285) -> Option<FieldOrigin> {
286 let PlaceBaseKey::Local(local) = place.base else {
287 return None;
288 };
289 if place.fields.is_empty() {
290 return None;
291 }
292 resolve_any_field_origin(tcx, caller, local, &place.fields)
293}
294
295fn self_borrow_mutability(
299 tcx: TyCtxt<'_>,
300 def_id: DefId,
301 self_local: Local,
302) -> Option<ty::Mutability> {
303 let body = tcx.optimized_mir(def_id);
304 match body.local_decls[self_local].ty.kind() {
305 TyKind::Ref(_, _, m) => Some(*m),
306 _ => None,
307 }
308}
309
310pub(super) fn escaped_self_field_violation(
311 tcx: TyCtxt<'_>,
312 current: DefId,
313 origin: &FieldOrigin,
314) -> Option<String> {
315 if public_raw_field(tcx, origin) {
316 return Some(format!(
317 "returned view escapes while raw field `{}` is public",
318 origin.field_name
319 ));
320 }
321 let current_self = self_borrow_mutability(tcx, current, Local::from_usize(1));
322 for impl_def_id in impls_for_struct(tcx, origin.struct_def_id) {
323 for item in tcx.associated_item_def_ids(impl_def_id) {
324 if *item == current {
325 continue;
326 }
327 if !matches!(tcx.def_kind(*item), DefKind::Fn | DefKind::AssocFn) {
328 continue;
329 }
330 if check_safety(tcx, *item) == Safety::Unsafe {
331 continue;
332 }
333 let Some(assoc) = tcx.opt_associated_item(*item) else {
334 continue;
335 };
336 if !matches!(assoc.kind, AssocKind::Fn { has_self: true, .. }) {
337 continue;
338 }
339 if !tcx.is_mir_available(*item) {
340 continue;
341 }
342 if let Some(reason) =
343 check_fn_against_field(tcx, *item, origin, current_self, Local::from_usize(1))
344 {
345 return Some(reason);
346 }
347 }
348 }
349 for (fn_def_id, param_locals) in free_fns_for_struct(tcx, origin.struct_def_id) {
353 if fn_def_id == current {
354 continue;
355 }
356 if check_safety(tcx, fn_def_id) == Safety::Unsafe {
357 continue;
358 }
359 if !tcx.is_mir_available(fn_def_id) {
360 continue;
361 }
362 for param_local in param_locals {
363 if let Some(reason) =
364 check_fn_against_field(tcx, fn_def_id, origin, current_self, param_local)
365 {
366 return Some(reason);
367 }
368 }
369 }
370 None
371}
372
373fn check_fn_against_field(
379 tcx: TyCtxt<'_>,
380 item: DefId,
381 origin: &FieldOrigin,
382 current_self: Option<ty::Mutability>,
383 self_local: Local,
384) -> Option<String> {
385 let item_self = self_borrow_mutability(tcx, item, self_local);
386 if method_writes_self_field(tcx, item, self_local, origin.field_index) {
387 if current_self.is_none() || item_self.is_none() {
388 return None;
389 }
390 if let (Some(ty::Mutability::Not), Some(ty::Mutability::Mut)) = (current_self, item_self) {
391 return None;
392 }
393 return Some(format!(
394 "safe fn `{}` writes through raw field `{}`",
395 tcx.def_path_str(item),
396 origin.field_name
397 ));
398 }
399 if method_exposes_self_field(tcx, item, self_local, origin.field_index) {
400 if current_self.is_none() || item_self.is_none() {
401 return None;
402 }
403 if let (Some(ty::Mutability::Not), Some(ty::Mutability::Mut)) = (current_self, item_self) {
404 return None;
405 }
406 if let (Some(ty::Mutability::Mut), Some(ty::Mutability::Mut)) = (current_self, item_self) {
407 return None;
408 }
409 return Some(format!(
410 "safe fn `{}` exposes raw field `{}`",
411 tcx.def_path_str(item),
412 origin.field_name
413 ));
414 }
415 None
416}
417
418fn public_raw_field(tcx: TyCtxt<'_>, origin: &FieldOrigin) -> bool {
419 let adt = tcx.adt_def(origin.struct_def_id);
420 let Some(field) = adt.all_fields().nth(origin.field_index) else {
421 return false;
422 };
423 field.vis.is_public()
424}
425
426fn impls_for_struct(tcx: TyCtxt<'_>, struct_def_id: DefId) -> Vec<DefId> {
427 let mut impls = tcx
428 .inherent_impls(struct_def_id)
429 .iter()
430 .copied()
431 .collect::<Vec<_>>();
432
433 for item_id in tcx.hir_crate_items(()).free_items() {
434 let item = tcx.hir_item(item_id);
435 let rustc_hir::ItemKind::Impl(impl_details) = &item.kind else {
436 continue;
437 };
438 let rustc_hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)) =
439 &impl_details.self_ty.kind
440 else {
441 continue;
442 };
443 let rustc_hir::def::Res::Def(_, def_id) = path.res else {
444 continue;
445 };
446 if def_id != struct_def_id {
447 continue;
448 }
449 let impl_def_id = item_id.owner_id.to_def_id();
450 if !impls.contains(&impl_def_id) {
451 impls.push(impl_def_id);
452 }
453 }
454
455 impls
456}
457
458fn free_fns_for_struct(tcx: TyCtxt<'_>, struct_def_id: DefId) -> Vec<(DefId, Vec<Local>)> {
463 let Some(struct_local) = struct_def_id.as_local() else {
464 return Vec::new();
465 };
466 let struct_module = tcx.parent_module_from_def_id(struct_local);
467 let mut fns = Vec::new();
468 for item_id in tcx.hir_crate_items(()).free_items() {
469 let item = tcx.hir_item(item_id);
470 let rustc_hir::ItemKind::Fn { .. } = &item.kind else {
471 continue;
472 };
473 let fn_def_id = item_id.owner_id.to_def_id();
474 let Some(fn_local) = fn_def_id.as_local() else {
475 continue;
476 };
477 if tcx.parent_module_from_def_id(fn_local) != struct_module {
478 continue;
479 }
480 let param_locals = struct_ref_param_locals(tcx, fn_def_id, struct_def_id);
481 if !param_locals.is_empty() {
482 fns.push((fn_def_id, param_locals));
483 }
484 }
485 fns
486}
487
488fn struct_ref_param_locals(tcx: TyCtxt<'_>, def_id: DefId, struct_def_id: DefId) -> Vec<Local> {
491 let body = tcx.optimized_mir(def_id);
492 (1..=body.arg_count)
493 .filter_map(|i| {
494 let local = Local::from_usize(i);
495 match body.local_decls[local].ty.kind() {
496 TyKind::Ref(_, pointee, _) => match pointee.kind() {
497 TyKind::Adt(adt_def, _) => (adt_def.did() == struct_def_id).then_some(local),
498 _ => None,
499 },
500 _ => None,
501 }
502 })
503 .collect()
504}
505
506fn method_writes_self_field(
509 tcx: TyCtxt<'_>,
510 method: DefId,
511 self_local: Local,
512 field_index: usize,
513) -> bool {
514 let body = tcx.optimized_mir(method);
515 let tree = crate::verify::vm::alias_tree::AliasTree::build(tcx, method);
516 let origin = self_field_key(self_local, field_index);
517
518 for block in body.basic_blocks.iter() {
519 for statement in &block.statements {
520 let StatementKind::Assign(assign) = &statement.kind else {
521 continue;
522 };
523 let (target, _) = assign.as_ref();
524 if place_is_raw_access_to_origin(target, &origin, &tree, &body.local_decls)
525 || place_raw_accesses_self_field(tcx, method, target, self_local, field_index)
526 {
527 return true;
528 }
529 }
530
531 let Some(terminator) = &block.terminator else {
532 continue;
533 };
534 if terminator_writes_origin(&terminator.kind, &origin, &tree) {
535 return true;
536 }
537 }
538
539 false
540}
541
542fn place_raw_accesses_self_field(
545 tcx: TyCtxt<'_>,
546 method: DefId,
547 place: &Place<'_>,
548 self_local: Local,
549 field_index: usize,
550) -> bool {
551 let body = tcx.optimized_mir(method);
552 let has_raw_deref = place.projection.iter().any(|projection| {
553 if let ProjectionElem::Deref = projection {
554 matches!(
555 body.local_decls[place.local].ty.kind(),
556 TyKind::RawPtr(_, _)
557 )
558 } else {
559 false
560 }
561 });
562 if !has_raw_deref {
563 return false;
564 }
565 local_traces_to_self_field(
566 tcx,
567 method,
568 place.local,
569 self_local,
570 field_index,
571 &mut HashSet::new(),
572 )
573}
574
575fn local_traces_to_self_field(
578 tcx: TyCtxt<'_>,
579 method: DefId,
580 local: Local,
581 self_local: Local,
582 field_index: usize,
583 seen: &mut HashSet<Local>,
584) -> bool {
585 if !seen.insert(local) {
586 return false;
587 }
588 let body = tcx.optimized_mir(method);
589 for block in body.basic_blocks.iter() {
590 for statement in &block.statements {
591 let StatementKind::Assign(assign) = &statement.kind else {
592 continue;
593 };
594 let (target, rvalue) = assign.as_ref();
595 if target.local != local {
596 continue;
597 }
598 let Some(source) = crate::helpers::mir_utils::rvalue_source_place(rvalue) else {
599 continue;
600 };
601 let source_key = PlaceKey::from_mir_place(source);
602 if source_key.base == PlaceBaseKey::Local(self_local.as_usize())
603 && source_key.fields.first() == Some(&field_index)
604 {
605 return true;
606 }
607 if source_key.fields.is_empty()
608 && local_traces_to_self_field(
609 tcx,
610 method,
611 source.local,
612 self_local,
613 field_index,
614 seen,
615 )
616 {
617 return true;
618 }
619 }
620 }
621 false
622}
623
624fn method_exposes_self_field(
628 tcx: TyCtxt<'_>,
629 method: DefId,
630 self_local: Local,
631 field_index: usize,
632) -> bool {
633 let body = tcx.optimized_mir(method);
634
635 let self_ty = body.local_decls[self_local].ty;
636 if !matches!(self_ty.kind(), TyKind::Ref(_, _, _)) {
637 return false;
638 }
639
640 let ret_ty = body.local_decls[Local::from_usize(0)].ty;
641 if !crate::helpers::mir_utils::type_contains_raw_ptr(tcx, ret_ty) {
642 return false;
643 }
644
645 let tree = crate::verify::vm::alias_tree::AliasTree::build(tcx, method);
646 let origin = self_field_key(self_local, field_index);
647
648 for block in body.basic_blocks.iter() {
649 for statement in &block.statements {
650 let StatementKind::Assign(assign) = &statement.kind else {
651 continue;
652 };
653 let (target, rvalue) = assign.as_ref();
654 if target.local.as_usize() == 0 && rvalue_mentions_origin(rvalue, &origin, &tree) {
655 return true;
656 }
657 }
658 }
659
660 false
661}
662
663fn rvalue_mentions_origin(
664 rvalue: &Rvalue<'_>,
665 origin: &PlaceKey,
666 tree: &crate::verify::vm::alias_tree::AliasTree,
667) -> bool {
668 rvalue_any_place_matching(rvalue, &mut |place| {
669 let key = PlaceKey::from_mir_place(place);
670 let resolved = if key.fields.is_empty() {
671 let (root, fields) = tree.resolve_local_to_root(place.local);
672 PlaceKey::from_origin(root, fields)
673 } else {
674 key
675 };
676 resolved.overlaps(origin)
677 })
678}
679
680fn self_field_key(self_local: Local, field_index: usize) -> PlaceKey {
682 PlaceKey {
683 base: PlaceBaseKey::Local(self_local.as_usize()),
684 fields: vec![field_index],
685 }
686}
687
688fn rvalue_mentions_local(
689 rvalue: &Rvalue<'_>,
690 local: Local,
691 aliases: &HashMap<Local, PlaceKey>,
692) -> bool {
693 crate::helpers::mir_utils::rvalue_any_place_matching(rvalue, &mut |place| {
694 let has_deref = place
697 .projection
698 .iter()
699 .any(|p| matches!(p, ProjectionElem::Deref));
700 !has_deref && (place.local == local || aliases.contains_key(&place.local))
701 })
702}
703
704fn raw_access_conflicts(kind: HazardKind, access: RawAccessKind) -> bool {
707 match kind {
708 HazardKind::SharedView => access == RawAccessKind::Write,
709 HazardKind::UniqueView => true,
710 }
711}
712
713pub(super) fn local_hazard_violation(
714 tcx: TyCtxt<'_>,
715 caller: DefId,
716 call_block: BasicBlock,
717 destination: Option<Local>,
718 origins: &[PlaceKey],
719 kind: HazardKind,
720 view_len_place: Option<PlaceKey>,
721) -> Option<String> {
722 local_hazard_violation_with(
723 tcx,
724 caller,
725 call_block,
726 destination,
727 origins,
728 kind,
729 false,
730 view_len_place,
731 )
732}
733
734fn local_hazard_violation_with(
735 tcx: TyCtxt<'_>,
736 caller: DefId,
737 call_block: BasicBlock,
738 destination: Option<Local>,
739 origins: &[PlaceKey],
740 kind: HazardKind,
741 strict_call_escape: bool,
742 view_len_place: Option<PlaceKey>,
743) -> Option<String> {
744 let body = tcx.optimized_mir(caller);
745 let tree = crate::verify::vm::alias_tree::AliasTree::build(tcx, caller);
746 let mut origins = origins.to_vec();
747 expand_origin_aliases(&tree, &mut origins);
748 let mut hazard_locals: HashSet<Local> = destination.into_iter().collect();
749 expand_hazard_alias_locals(tcx, caller, &mut hazard_locals);
750 for data in body.basic_blocks.iter() {
751 if let Some(terminator) = &data.terminator {
752 if let TerminatorKind::Call {
753 func,
754 destination: call_dest,
755 ..
756 } = &terminator.kind
757 {
758 if crate::verify::api_classify::is_split_at(
759 crate::helpers::mir_utils::dep_callee_def_id(func),
760 ) {
761 hazard_locals.insert(call_dest.local);
762 }
763 }
764 }
765 }
766 origins.retain(|origin| !origin.local().is_some_and(|l| hazard_locals.contains(&l)));
767 let vec_owners = find_as_ptr_receivers(tcx, caller, &origins, &tree, true);
768 let reachable = blocks_reachable_after_call(tcx, caller, call_block);
769
770 for (block_index, block) in reverse_postorder_blocks(body) {
771 if !reachable.contains(&block_index) {
772 continue;
773 }
774 for (statement_index, statement) in block.statements.iter().enumerate() {
775 match &statement.kind {
776 StatementKind::StorageDead(local) => {
777 hazard_locals.remove(local);
778 }
779 StatementKind::Assign(assign) => {
780 let (target, rvalue) = assign.as_ref();
781 if rvalue_mentions_any_local(rvalue, &hazard_locals) {
782 let target_ty = body.local_decls[target.local].ty;
783 if matches!(
784 target_ty.kind(),
785 TyKind::Ref(_, _, _) | TyKind::RawPtr(_, _)
786 ) {
787 hazard_locals.insert(target.local);
788 }
789 }
790 if !hazard_locals.is_empty()
791 && !hazard_locals.contains(&target.local)
792 && raw_access_conflicts(kind, RawAccessKind::Write)
793 && place_is_raw_access_to_any_origin(
794 target,
795 &origins,
796 &tree,
797 &body.local_decls,
798 )
799 && hazard_used_after_statement(
800 tcx,
801 caller,
802 block_index,
803 statement_index,
804 &hazard_locals,
805 )
806 {
807 return Some(format!(
808 "raw write through original pointer after {:?} view creation",
809 kind
810 ));
811 }
812 if !hazard_locals.is_empty()
813 && !hazard_locals.contains(&target.local)
814 && raw_access_conflicts(kind, RawAccessKind::Read)
815 && !crate::helpers::mir_utils::rvalue_source_place(rvalue)
816 .is_some_and(|place| hazard_locals.contains(&place.local))
817 && !rvalue_reads_like_view(rvalue, tcx, caller, &origins, &tree)
818 && rvalue_reads_any_origin(rvalue, &origins, &tree, &body.local_decls)
819 && hazard_used_after_statement(
820 tcx,
821 caller,
822 block_index,
823 statement_index,
824 &hazard_locals,
825 )
826 {
827 return Some(format!(
828 "raw read through original pointer after {:?} view creation",
829 kind
830 ));
831 }
832 }
833 _ => {}
834 }
835 }
836
837 if !hazard_locals.is_empty() {
838 let Some(terminator) = &block.terminator else {
839 continue;
840 };
841 if origins
842 .iter()
843 .any(|origin| terminator_writes_origin(&terminator.kind, origin, &tree))
844 && hazard_used_after_block(tcx, caller, block_index, &hazard_locals)
845 {
846 return Some(format!(
847 "raw write call through original pointer after {:?} view creation",
848 kind
849 ));
850 }
851 if kind == HazardKind::UniqueView
852 && !vec_owners.is_empty()
853 && terminator_invalidates_vec_owner(&terminator.kind, &vec_owners, &tree)
854 && hazard_used_after_block(tcx, caller, block_index, &hazard_locals)
855 {
856 return Some(
857 "Vec may reallocate while a raw-derived mutable view is still live".to_string(),
858 );
859 }
860 if strict_call_escape
861 && block_index != call_block
862 && !terminator_is_benign_origin_use(&terminator.kind)
863 && origins
864 .iter()
865 .any(|origin| terminator_uses_origin(&terminator.kind, origin, &tree))
866 && hazard_used_after_block(tcx, caller, block_index, &hazard_locals)
867 {
868 return Some(format!(
869 "raw pointer escapes to another call while the {:?} view is live",
870 kind
871 ));
872 }
873 if view_len_place.is_some() {
874 if let TerminatorKind::Call {
875 func,
876 args,
877 destination: call_dest,
878 ..
879 } = &terminator.kind
880 {
881 let callee = crate::helpers::mir_utils::dep_callee_def_id(func);
882 if crate::verify::api_classify::is_from_raw_parts(callee) && args.len() >= 1 {
883 if let Some(ptr_place) = operand_place(&args[0].node) {
884 let offset_eq = is_ptr_add_offset_eq(
885 tcx,
886 caller,
887 &ptr_place,
888 view_len_place.as_ref().unwrap(),
889 );
890 let from_add = is_ptr_from_ptr_add(tcx, caller, &ptr_place);
891 if offset_eq || from_add {
892 hazard_locals.insert(call_dest.local);
893 continue;
894 }
895 }
896 }
897 }
898 }
899 }
900 }
901
902 None
903}
904
905fn reverse_postorder_blocks<'a, 'tcx>(
906 body: &'a rustc_middle::mir::Body<'tcx>,
907) -> impl Iterator<Item = (BasicBlock, &'a rustc_middle::mir::BasicBlockData<'tcx>)> {
908 rustc_middle::mir::traversal::reverse_postorder(body).map(|(block, data)| (block, data))
909}
910
911pub(crate) fn live_locals_at(
917 tcx: TyCtxt<'_>,
918 caller: DefId,
919 call_block: BasicBlock,
920 statement_index: usize,
921 seed_params: bool,
922 track_moves: bool,
923) -> HashSet<Local> {
924 let body = tcx.optimized_mir(caller);
925 let mut live: HashSet<Local> = if seed_params {
929 (1..=body.arg_count).map(Local::from_usize).collect()
930 } else {
931 HashSet::new()
932 };
933 for (block, data) in reverse_postorder_blocks(body) {
934 let reached = block == call_block;
935 for (i, statement) in data.statements.iter().enumerate() {
936 if reached && i >= statement_index {
937 break;
938 }
939 match &statement.kind {
940 StatementKind::StorageLive(local) => {
941 live.insert(*local);
942 }
943 StatementKind::StorageDead(local) => {
944 live.remove(local);
945 }
946 StatementKind::Assign(assign) if track_moves => {
947 let (_, rvalue) = &**assign;
950 if let rustc_middle::mir::Rvalue::Use(operand, ..) = rvalue {
951 if let Operand::Move(place) = operand {
952 live.remove(&place.local);
953 }
954 }
955 }
956 _ => {}
957 }
958 }
959 if reached {
960 break;
961 }
962 if track_moves {
965 if let TerminatorKind::Call { args, .. } = &data.terminator().kind {
966 for arg in args {
967 if let Operand::Move(place) = &arg.node {
968 live.remove(&place.local);
969 }
970 }
971 }
972 }
973 }
974 live
975}
976
977fn expand_origin_aliases(
978 tree: &crate::verify::vm::alias_tree::AliasTree,
979 origins: &mut Vec<PlaceKey>,
980) {
981 let mut changed = true;
982 while changed {
983 changed = false;
984 for node in &tree.nodes {
985 let local_key = PlaceKey {
986 base: PlaceBaseKey::Local(node.local.as_usize()),
987 fields: Vec::new(),
988 };
989 let (root, fields) = tree.resolve_local_to_root(node.local);
990 let alias = PlaceKey::from_origin(root, fields);
991 let related = origins.iter().any(|origin| {
992 local_key.overlaps(origin)
993 || origin.overlaps(&local_key)
994 || alias.overlaps(origin)
995 || origin.overlaps(&alias)
996 });
997 if !related {
998 continue;
999 }
1000 if !origins.contains(&local_key) {
1001 origins.push(local_key);
1002 changed = true;
1003 }
1004 if !origins.contains(&alias) {
1005 origins.push(alias);
1006 changed = true;
1007 }
1008 }
1009 }
1010}
1011
1012fn expand_hazard_alias_locals(tcx: TyCtxt<'_>, caller: DefId, hazard_locals: &mut HashSet<Local>) {
1013 let body = tcx.optimized_mir(caller);
1014 let mut changed = true;
1015 while changed {
1016 changed = false;
1017 for block in body.basic_blocks.iter() {
1018 for statement in &block.statements {
1019 let StatementKind::Assign(assign) = &statement.kind else {
1020 continue;
1021 };
1022 let (target, rvalue) = assign.as_ref();
1023 if rvalue_mentions_any_local(rvalue, hazard_locals)
1024 && hazard_locals.insert(target.local)
1025 {
1026 changed = true;
1027 }
1028 }
1029 }
1030 }
1031}
1032
1033fn rvalue_mentions_any_local(rvalue: &Rvalue<'_>, locals: &HashSet<Local>) -> bool {
1034 rvalue_any_place_matching(rvalue, &mut |place| locals.contains(&place.local))
1035}
1036
1037fn hazard_used_after_statement(
1038 tcx: TyCtxt<'_>,
1039 caller: DefId,
1040 block: BasicBlock,
1041 statement_index: usize,
1042 hazard_locals: &HashSet<Local>,
1043) -> bool {
1044 let body = tcx.optimized_mir(caller);
1045 let data = &body.basic_blocks[block];
1046 for statement in data.statements.iter().skip(statement_index + 1) {
1047 if statement_uses_any_local(statement, hazard_locals) {
1048 return true;
1049 }
1050 }
1051 let terminator = data.terminator();
1052 if terminator_uses_any_local(&terminator.kind, hazard_locals) {
1053 return true;
1054 }
1055 hazard_used_after_block(tcx, caller, block, hazard_locals)
1056}
1057
1058fn hazard_used_after_block(
1059 tcx: TyCtxt<'_>,
1060 caller: DefId,
1061 start: BasicBlock,
1062 hazard_locals: &HashSet<Local>,
1063) -> bool {
1064 let body = tcx.optimized_mir(caller);
1065 let mut seen = HashSet::new();
1066 let mut stack: Vec<_> = body.basic_blocks[start].terminator().successors().collect();
1067
1068 while let Some(block) = stack.pop() {
1069 if !seen.insert(block) {
1070 continue;
1071 }
1072 let data = &body.basic_blocks[block];
1073 for statement in &data.statements {
1074 if statement_uses_any_local(statement, hazard_locals) {
1075 return true;
1076 }
1077 }
1078 let terminator = data.terminator();
1079 if terminator_uses_any_local(&terminator.kind, hazard_locals) {
1080 return true;
1081 }
1082 stack.extend(terminator.successors());
1083 }
1084
1085 false
1086}
1087
1088fn statement_uses_any_local(
1089 statement: &rustc_middle::mir::Statement<'_>,
1090 locals: &HashSet<Local>,
1091) -> bool {
1092 let StatementKind::Assign(assign) = &statement.kind else {
1093 return false;
1094 };
1095 let (target, rvalue) = assign.as_ref();
1096 locals.contains(&target.local) || rvalue_mentions_any_local(rvalue, locals)
1097}
1098
1099fn terminator_uses_any_local(terminator: &TerminatorKind<'_>, locals: &HashSet<Local>) -> bool {
1100 match terminator {
1101 TerminatorKind::Call { args, .. } => args.iter().any(|arg| match &arg.node {
1102 Operand::Copy(place) | Operand::Move(place) => locals.contains(&place.local),
1103 Operand::Constant(_) => false,
1104 #[cfg(rapx_ge_95)]
1105 Operand::RuntimeChecks(_) => false,
1106 }),
1107 TerminatorKind::SwitchInt { discr, .. } | TerminatorKind::Assert { cond: discr, .. } => {
1108 match discr {
1109 Operand::Copy(place) | Operand::Move(place) => locals.contains(&place.local),
1110 Operand::Constant(_) => false,
1111 #[cfg(rapx_ge_95)]
1112 Operand::RuntimeChecks(_) => false,
1113 }
1114 }
1115 TerminatorKind::Drop { place, .. } => locals.contains(&place.local),
1116 _ => false,
1117 }
1118}
1119
1120fn resolve_mir_place_tree(
1123 tree: &crate::verify::vm::alias_tree::AliasTree,
1124 place: &Place<'_>,
1125) -> PlaceKey {
1126 let fields = PlaceKey::from_mir_place(place).fields;
1127 let (root, root_fields) = resolve_via_tree(tree, place.local, &fields);
1128 PlaceKey::from_origin(root, root_fields)
1129}
1130
1131fn resolve_place_key_tree(
1134 tree: &crate::verify::vm::alias_tree::AliasTree,
1135 place: &Place<'_>,
1136) -> PlaceKey {
1137 if tree.tag_of(place.local).is_some() {
1138 let (root, fields) = tree.resolve_local_to_root(place.local);
1139 PlaceKey::from_origin(root, fields)
1140 } else {
1141 PlaceKey::from_mir_place(place)
1142 }
1143}
1144
1145fn place_is_raw_access_to_any_origin(
1146 place: &Place<'_>,
1147 origins: &[PlaceKey],
1148 tree: &crate::verify::vm::alias_tree::AliasTree,
1149 local_decls: &LocalDecls<'_>,
1150) -> bool {
1151 origins
1152 .iter()
1153 .any(|origin| place_is_raw_access_to_origin(place, origin, tree, local_decls))
1154}
1155
1156fn place_is_raw_access_to_origin(
1157 place: &Place<'_>,
1158 origin: &PlaceKey,
1159 tree: &crate::verify::vm::alias_tree::AliasTree,
1160 local_decls: &LocalDecls<'_>,
1161) -> bool {
1162 let local = place.local;
1163 let has_raw_deref = place.projection.iter().any(|projection| {
1164 if let ProjectionElem::Deref = projection {
1165 matches!(local_decls[local].ty.kind(), TyKind::RawPtr(_, _))
1166 } else {
1167 false
1168 }
1169 });
1170 if !has_raw_deref {
1171 return false;
1172 }
1173 let pointer = resolve_place_key_tree(tree, place);
1174 pointer.overlaps(origin)
1175}
1176
1177fn rvalue_reads_like_view(
1178 rvalue: &Rvalue<'_>,
1179 tcx: TyCtxt<'_>,
1180 caller: DefId,
1181 origins: &[PlaceKey],
1182 tree: &crate::verify::vm::alias_tree::AliasTree,
1183) -> bool {
1184 let Some(place) = crate::helpers::mir_utils::rvalue_source_place(rvalue) else {
1185 return false;
1186 };
1187 if !place
1188 .projection
1189 .iter()
1190 .any(|p| matches!(p, ProjectionElem::Deref))
1191 {
1192 return false;
1193 }
1194 let pointer = resolve_place_key_tree(tree, place);
1195 if !origins.iter().any(|origin| pointer.overlaps(origin)) {
1196 return false;
1197 }
1198 is_origin_a_reference(tcx, caller, &pointer)
1199}
1200
1201fn rvalue_reads_any_origin(
1202 rvalue: &Rvalue<'_>,
1203 origins: &[PlaceKey],
1204 tree: &crate::verify::vm::alias_tree::AliasTree,
1205 local_decls: &LocalDecls<'_>,
1206) -> bool {
1207 rvalue_any_place_matching(rvalue, &mut |place| {
1208 place_is_raw_access_to_any_origin(place, origins, tree, local_decls)
1209 })
1210}
1211
1212fn terminator_writes_origin<'tcx>(
1213 terminator: &TerminatorKind<'tcx>,
1214 origin: &PlaceKey,
1215 tree: &crate::verify::vm::alias_tree::AliasTree,
1216) -> bool {
1217 let TerminatorKind::Call { func, args, .. } = terminator else {
1218 return false;
1219 };
1220 let callee = crate::helpers::mir_utils::dep_callee_def_id(func);
1221 if !crate::verify::api_classify::is_ptr_write(callee) {
1222 return false;
1223 }
1224 let Some(arg0) = args.first() else {
1225 return false;
1226 };
1227 let Some(place) = operand_mir_place(&arg0.node) else {
1228 return false;
1229 };
1230 resolve_mir_place_tree(tree, place).overlaps(origin)
1231}
1232
1233fn terminator_uses_origin<'tcx>(
1234 terminator: &TerminatorKind<'tcx>,
1235 origin: &PlaceKey,
1236 tree: &crate::verify::vm::alias_tree::AliasTree,
1237) -> bool {
1238 let TerminatorKind::Call { args, .. } = terminator else {
1239 return false;
1240 };
1241 args.iter().any(|arg| {
1242 let Some(place) = operand_mir_place(&arg.node) else {
1243 return false;
1244 };
1245 resolve_mir_place_tree(tree, place).overlaps(origin)
1246 })
1247}
1248
1249fn terminator_is_benign_origin_use<'tcx>(
1250 terminator: &TerminatorKind<'tcx>,
1251) -> bool {
1252 let TerminatorKind::Call { func, .. } = terminator else {
1253 return true;
1254 };
1255 crate::verify::api_classify::is_benign_origin_use(crate::helpers::mir_utils::dep_callee_def_id(
1256 func,
1257 ))
1258}
1259
1260fn terminator_invalidates_vec_owner<'tcx>(
1261 terminator: &TerminatorKind<'tcx>,
1262 owners: &[PlaceKey],
1263 tree: &crate::verify::vm::alias_tree::AliasTree,
1264) -> bool {
1265 let TerminatorKind::Call { func, args, .. } = terminator else {
1266 return false;
1267 };
1268 if !crate::verify::api_classify::is_vec_invalidating_method(
1269 crate::helpers::mir_utils::dep_callee_def_id(func),
1270 ) {
1271 return false;
1272 }
1273 args.iter().any(|arg| {
1274 let Some(place) = operand_mir_place(&arg.node) else {
1275 return false;
1276 };
1277 let arg = resolve_mir_place_tree(tree, place);
1278 owners
1279 .iter()
1280 .any(|owner| arg.overlaps(owner) || owner.overlaps(&arg))
1281 })
1282}
1283
1284fn find_as_ptr_receivers(
1285 tcx: TyCtxt<'_>,
1286 caller: DefId,
1287 origins: &[PlaceKey],
1288 tree: &crate::verify::vm::alias_tree::AliasTree,
1289 check_alias_dest: bool,
1290) -> Vec<PlaceKey> {
1291 let body = tcx.optimized_mir(caller);
1292 let mut result = Vec::new();
1293 for block in body.basic_blocks.iter() {
1294 let Some(terminator) = &block.terminator else {
1295 continue;
1296 };
1297 let TerminatorKind::Call {
1298 func,
1299 args,
1300 destination,
1301 ..
1302 } = &terminator.kind
1303 else {
1304 continue;
1305 };
1306 if !crate::verify::api_classify::is_as_ptr(crate::helpers::mir_utils::dep_callee_def_id(
1307 func,
1308 )) {
1309 continue;
1310 }
1311 let destination_key = PlaceKey {
1312 base: PlaceBaseKey::Local(destination.local.as_usize()),
1313 fields: Vec::new(),
1314 };
1315 let dest_overlaps = || {
1316 origins
1317 .iter()
1318 .any(|origin| destination_key.overlaps(origin))
1319 || (check_alias_dest
1320 && tree.tag_of(destination.local).is_some_and(|_| {
1321 let (root, fields) = tree.resolve_local_to_root(destination.local);
1322 let alias = PlaceKey::from_origin(root, fields);
1323 origins.iter().any(|o| alias.overlaps(o))
1324 }))
1325 };
1326 if !dest_overlaps() {
1327 continue;
1328 }
1329 let Some(receiver) = args.first() else {
1330 continue;
1331 };
1332 let Some(place) = operand_mir_place(&receiver.node) else {
1333 continue;
1334 };
1335 let resolved = resolve_mir_place_tree(tree, place);
1336 if !result.contains(&resolved) {
1337 result.push(resolved);
1338 }
1339 }
1340 result
1341}
1342
1343fn is_ptr_add_offset_eq(
1344 tcx: TyCtxt<'_>,
1345 caller: DefId,
1346 ptr_place: &PlaceKey,
1347 view_len: &PlaceKey,
1348) -> bool {
1349 let body = tcx.optimized_mir(caller);
1350 let tree = crate::verify::vm::alias_tree::AliasTree::build(tcx, caller);
1351 let view_len_root = view_len.local().map(|l| tree.resolve_local_to_root(l));
1352 for (_bb, data) in body.basic_blocks.iter_enumerated() {
1353 if let TerminatorKind::Call {
1354 func,
1355 args,
1356 destination,
1357 ..
1358 } = &data.terminator().kind
1359 {
1360 let ptr_key = PlaceKey::from_mir_place(destination);
1361 if ptr_key != *ptr_place {
1362 continue;
1363 }
1364 if crate::verify::api_classify::is_pointer_add(
1365 crate::helpers::mir_utils::dep_callee_def_id(func),
1366 ) && args.len() >= 2
1367 {
1368 if let Some(offset_place) = operand_place(&args[1].node) {
1369 let offset_root = offset_place.local().map(|l| tree.resolve_local_to_root(l));
1370 return offset_root == view_len_root;
1371 }
1372 }
1373 }
1374 }
1375 false
1376}
1377
1378fn is_ptr_from_ptr_add(tcx: TyCtxt<'_>, caller: DefId, ptr_place: &PlaceKey) -> bool {
1379 let body = tcx.optimized_mir(caller);
1380 for (_bb, data) in body.basic_blocks.iter_enumerated() {
1381 if let TerminatorKind::Call {
1382 func, destination, ..
1383 } = &data.terminator().kind
1384 {
1385 let ptr_key = PlaceKey::from_mir_place(destination);
1386 if ptr_key != *ptr_place {
1387 continue;
1388 }
1389 return crate::verify::api_classify::is_pointer_add(
1390 crate::helpers::mir_utils::dep_callee_def_id(func),
1391 );
1392 }
1393 }
1394 false
1395}
1396
1397pub(super) fn ownership_transfer_violation(
1400 tcx: TyCtxt<'_>,
1401 caller: DefId,
1402 call_block: BasicBlock,
1403 destination: Option<Local>,
1404 origin_place: &PlaceKey,
1405) -> Option<String> {
1406 let body = tcx.optimized_mir(caller);
1407 let mut owner_locals: HashSet<Local> = destination.into_iter().collect();
1408 expand_hazard_alias_locals(tcx, caller, &mut owner_locals);
1409 let reachable = blocks_reachable_after_call(tcx, caller, call_block);
1410
1411 for block_index in &reachable {
1412 if let Some(terminator) = &body.basic_blocks[*block_index].terminator
1413 && terminator_returns_ownership(&terminator.kind, &owner_locals)
1414 {
1415 return None;
1416 }
1417 }
1418
1419 let origins = places_holding_transferred_pointer(tcx, caller, call_block, origin_place);
1420
1421 if let Some(reason) = pre_existing_view_on_origin(tcx, caller, call_block, &reachable, &origins)
1422 {
1423 return Some(reason);
1424 }
1425
1426 let start = match &body.basic_blocks[call_block].terminator().kind {
1427 TerminatorKind::Call {
1428 target: Some(target),
1429 ..
1430 } => *target,
1431 _ => return None,
1432 };
1433
1434 let mut entry_states: HashMap<BasicBlock, Vec<PlaceKey>> = HashMap::new();
1435 let mut worklist: Vec<(BasicBlock, Vec<PlaceKey>)> = vec![(start, origins)];
1436
1437 while let Some((block_index, incoming)) = worklist.pop() {
1438 let mut live_origins = match entry_states.get_mut(&block_index) {
1439 Some(known) => {
1440 let mut changed = false;
1441 for origin in &incoming {
1442 if !known.contains(origin) {
1443 known.push(origin.clone());
1444 changed = true;
1445 }
1446 }
1447 if !changed {
1448 continue;
1449 }
1450 known.clone()
1451 }
1452 None => {
1453 entry_states.insert(block_index, incoming.clone());
1454 incoming
1455 }
1456 };
1457
1458 let block = &body.basic_blocks[block_index];
1459 for statement in &block.statements {
1460 match &statement.kind {
1461 StatementKind::Assign(assign) => {
1462 let (target, rvalue) = assign.as_ref();
1463 let target_key = PlaceKey::from_mir_place(target);
1464 let is_deref_to_pointee = target_key.fields.is_empty()
1465 && target
1466 .projection
1467 .iter()
1468 .any(|p| matches!(p, ProjectionElem::Deref));
1469 if !is_deref_to_pointee {
1470 live_origins.retain(|origin| !place_key_is_prefix_of(&target_key, origin));
1471 }
1472 if place_is_raw_access_to_live_origin(target, &live_origins)
1473 || rvalue_any_place_matching(rvalue, &mut |place| {
1474 place_is_raw_access_to_live_origin(place, &live_origins)
1475 })
1476 {
1477 return Some(
1478 "raw pointer reused after ownership was transferred to an owning value"
1479 .into(),
1480 );
1481 }
1482 let copies_origin = rvalue_copies_live_origin_value(rvalue, &live_origins);
1483 kill_strongly_updated_origins(&body.local_decls, target, &mut live_origins);
1484 if copies_origin
1485 && !target
1486 .projection
1487 .iter()
1488 .any(|projection| matches!(projection, ProjectionElem::Deref))
1489 {
1490 let target_key = PlaceKey::from_mir_place(target);
1491 if !live_origins.contains(&target_key) {
1492 live_origins.push(target_key);
1493 }
1494 }
1495 }
1496 StatementKind::StorageDead(local) => {
1497 live_origins
1498 .retain(|origin| origin.base != PlaceBaseKey::Local(local.as_usize()));
1499 }
1500 _ => {}
1501 }
1502 }
1503
1504 let Some(terminator) = &block.terminator else {
1505 continue;
1506 };
1507 if terminator_uses_live_origin(&terminator.kind, &live_origins) {
1508 return Some(
1509 "raw pointer passed to another call after ownership was transferred".into(),
1510 );
1511 }
1512 if let TerminatorKind::Call {
1513 destination: call_destination,
1514 ..
1515 } = &terminator.kind
1516 {
1517 kill_strongly_updated_origins(&body.local_decls, call_destination, &mut live_origins);
1518 }
1519 if live_origins.is_empty() {
1520 continue;
1521 }
1522 for successor in terminator.successors() {
1523 worklist.push((successor, live_origins.clone()));
1524 }
1525 }
1526
1527 None
1528}
1529
1530fn places_holding_transferred_pointer(
1531 tcx: TyCtxt<'_>,
1532 caller: DefId,
1533 call_block: BasicBlock,
1534 origin_place: &PlaceKey,
1535) -> Vec<PlaceKey> {
1536 let body = tcx.optimized_mir(caller);
1537 let mut holders = vec![origin_place.clone()];
1538 let mut killed: HashSet<Local> = HashSet::new();
1539 let mut block_index = call_block;
1540
1541 loop {
1542 let block = &body.basic_blocks[block_index];
1543 for statement in block.statements.iter().rev() {
1544 let StatementKind::Assign(assign) = &statement.kind else {
1545 continue;
1546 };
1547 let (target, rvalue) = assign.as_ref();
1548 if target
1549 .projection
1550 .iter()
1551 .any(|projection| matches!(projection, ProjectionElem::Deref))
1552 {
1553 continue;
1554 }
1555 let target_key = PlaceKey::from_mir_place(target);
1556 let target_defines_holder =
1557 !killed.contains(&target.local) && holders.iter().any(|h| target_key.overlaps(h));
1558
1559 let source_place = crate::helpers::mir_utils::rvalue_source_place(rvalue);
1560
1561 if target_defines_holder {
1562 if let Some(source) = source_place
1563 && !killed.contains(&source.local)
1564 {
1565 let source_key = PlaceKey::from_mir_place(source);
1566 for holder in holders.clone() {
1567 if let Some(spliced) =
1568 splice_holder_fields(&target_key, &holder, &source_key)
1569 && !holders.contains(&spliced)
1570 {
1571 holders.push(spliced);
1572 }
1573 }
1574 }
1575 } else if let Some(source) = source_place
1576 && !killed.contains(&target.local)
1577 && !source
1578 .projection
1579 .iter()
1580 .any(|projection| matches!(projection, ProjectionElem::Deref))
1581 {
1582 let source_key = PlaceKey::from_mir_place(source);
1583 if holders.iter().any(|h| source_key.overlaps(h)) && !holders.contains(&target_key)
1584 {
1585 holders.push(target_key.clone());
1586 }
1587 }
1588 killed.insert(target.local);
1589 }
1590
1591 let predecessors = &body.basic_blocks.predecessors()[block_index];
1592 if predecessors.len() != 1 {
1593 break;
1594 }
1595 block_index = predecessors[0];
1596 let terminator = body.basic_blocks[block_index].terminator();
1597 if let TerminatorKind::Call {
1598 func,
1599 args,
1600 destination: call_destination,
1601 ..
1602 } = &terminator.kind
1603 {
1604 let destination_key = PlaceKey::from_mir_place(call_destination);
1605 if !killed.contains(&call_destination.local)
1606 && holders.iter().any(|h| destination_key.overlaps(h))
1607 {
1608 if crate::verify::api_classify::is_as_ptr(
1609 crate::helpers::mir_utils::dep_callee_def_id(func),
1610 ) && let Some(arg) = args.first()
1611 && let Operand::Copy(place) | Operand::Move(place) = &arg.node
1612 && !killed.contains(&place.local)
1613 {
1614 let key = PlaceKey::from_mir_place(place);
1615 if !holders.contains(&key) {
1616 holders.push(key);
1617 }
1618 }
1619 }
1620 killed.insert(call_destination.local);
1621 }
1622 }
1623
1624 holders
1625}
1626
1627fn splice_holder_fields(
1628 target: &PlaceKey,
1629 holder: &PlaceKey,
1630 source: &PlaceKey,
1631) -> Option<PlaceKey> {
1632 if !place_key_is_prefix_of(target, holder) {
1633 return None;
1634 }
1635 let mut fields = source.fields.clone();
1636 fields.extend_from_slice(&holder.fields[target.fields.len()..]);
1637 Some(PlaceKey {
1638 base: source.base.clone(),
1639 fields,
1640 })
1641}
1642
1643fn kill_strongly_updated_origins(
1644 local_decls: &LocalDecls<'_>,
1645 target: &Place<'_>,
1646 live_origins: &mut Vec<PlaceKey>,
1647) {
1648 let deref_count = target
1649 .projection
1650 .iter()
1651 .filter(|p| matches!(p, ProjectionElem::Deref))
1652 .count();
1653 if deref_count == 0 {
1654 let target_key = PlaceKey::from_mir_place(target);
1655 live_origins.retain(|origin| !place_key_is_prefix_of(&target_key, origin));
1656 return;
1657 }
1658 if deref_count == 1 && matches!(target.projection[0], ProjectionElem::Deref) {
1659 let ty = local_decls[target.local].ty;
1660 if matches!(ty.kind(), ty::Ref(_, _, ty::Mutability::Mut)) {
1661 let target_key = PlaceKey::from_mir_place(target);
1662 live_origins.retain(|origin| !place_key_is_prefix_of(&target_key, origin));
1663 }
1664 }
1665}
1666
1667fn place_key_is_prefix_of(prefix: &PlaceKey, place: &PlaceKey) -> bool {
1668 prefix.base == place.base
1669 && prefix.fields.len() <= place.fields.len()
1670 && place.fields[..prefix.fields.len()] == prefix.fields[..]
1671}
1672
1673fn place_is_raw_access_to_live_origin(place: &Place<'_>, live_origins: &[PlaceKey]) -> bool {
1674 if !place
1675 .projection
1676 .iter()
1677 .any(|projection| matches!(projection, ProjectionElem::Deref))
1678 {
1679 return false;
1680 }
1681 let key = PlaceKey::from_mir_place(place);
1682 live_origins.iter().any(|origin| key.overlaps(origin))
1683}
1684
1685fn rvalue_copies_live_origin_value(rvalue: &Rvalue<'_>, live_origins: &[PlaceKey]) -> bool {
1686 let Some(place) = crate::helpers::mir_utils::rvalue_source_place(rvalue) else {
1687 return false;
1688 };
1689 if place
1690 .projection
1691 .iter()
1692 .any(|projection| matches!(projection, ProjectionElem::Deref))
1693 {
1694 return false;
1695 }
1696 let key = PlaceKey::from_mir_place(place);
1697 live_origins.iter().any(|origin| key.overlaps(origin))
1698}
1699
1700fn terminator_uses_live_origin(kind: &TerminatorKind<'_>, live_origins: &[PlaceKey]) -> bool {
1701 let TerminatorKind::Call { args, .. } = kind else {
1702 return false;
1703 };
1704 args.iter().any(|arg| {
1705 let Some(place) = operand_mir_place(&arg.node) else {
1706 return false;
1707 };
1708 let key = PlaceKey::from_mir_place(place);
1709 live_origins.iter().any(|origin| key.overlaps(origin))
1710 })
1711}
1712
1713fn terminator_returns_ownership(
1714 terminator: &TerminatorKind<'_>,
1715 owner_locals: &HashSet<Local>,
1716) -> bool {
1717 let TerminatorKind::Call { func, args, .. } = terminator else {
1718 return false;
1719 };
1720 if !crate::verify::api_classify::is_ownership_return(
1721 crate::helpers::mir_utils::dep_callee_def_id(func),
1722 ) {
1723 return false;
1724 }
1725 args.iter().any(|arg| match &arg.node {
1726 Operand::Copy(place) | Operand::Move(place) => owner_locals.contains(&place.local),
1727 _ => false,
1728 })
1729}
1730
1731fn pre_existing_view_on_origin(
1732 tcx: TyCtxt<'_>,
1733 caller: DefId,
1734 call_block: BasicBlock,
1735 reachable_after: &HashSet<BasicBlock>,
1736 origin_holders: &[PlaceKey],
1737) -> Option<String> {
1738 let body = tcx.optimized_mir(caller);
1739 let tree = crate::verify::vm::alias_tree::AliasTree::build(tcx, caller);
1740
1741 let holder_origins: Vec<(usize, Vec<usize>)> = origin_holders
1742 .iter()
1743 .flat_map(|h| {
1744 if let PlaceBaseKey::Local(l) = h.base {
1745 let resolved = resolve_via_tree(&tree, Local::from_usize(l), &h.fields);
1746 if resolved.0 == 1 && !resolved.1.is_empty() {
1747 Some(resolved)
1748 } else {
1749 None
1750 }
1751 } else {
1752 None
1753 }
1754 })
1755 .collect();
1756
1757 for (bb, data) in body.basic_blocks.iter_enumerated() {
1758 if reachable_after.contains(&bb) || bb == call_block {
1759 continue;
1760 }
1761 let terminator = data.terminator();
1762 if let TerminatorKind::Call { func, args, .. } = &terminator.kind {
1763 let callee_name = crate::helpers::mir_utils::call_name(tcx, func);
1764 if crate::verify::api_classify::is_nonnull_as_ref_as_mut(
1765 crate::helpers::mir_utils::dep_callee_def_id(func),
1766 ) {
1767 if let Some(arg) = args.first()
1768 && let Some(place) = operand_mir_place(&arg.node)
1769 {
1770 let arg_resolved = resolve_via_tree(
1771 &tree,
1772 place.local,
1773 &PlaceKey::from_mir_place(place).fields,
1774 );
1775 if arg_resolved.0 == 1
1776 && !arg_resolved.1.is_empty()
1777 && holder_origins
1778 .iter()
1779 .any(|(h, hf)| *h == arg_resolved.0 && *hf == arg_resolved.1)
1780 {
1781 return Some(format!(
1782 "pre-existing view from {} aliases the ownership-transferred pointer",
1783 callee_name,
1784 ));
1785 }
1786 }
1787 }
1788 }
1789
1790 for statement in &data.statements {
1791 let StatementKind::Assign(assign) = &statement.kind else {
1792 continue;
1793 };
1794 let (_target, rvalue) = assign.as_ref();
1795 let src_place: Option<&Place<'_>> = match rvalue {
1796 Rvalue::Ref(_, _, place) => Some(place),
1797 Rvalue::Cast(rustc_middle::mir::CastKind::PtrToPtr, operand, _) => {
1798 match operand {
1799 Operand::Copy(place) | Operand::Move(place) => Some(place),
1800 _ => None,
1801 }
1802 }
1803 _ => None,
1804 };
1805 let Some(place) = src_place else {
1806 continue;
1807 };
1808 if !place
1809 .projection
1810 .iter()
1811 .any(|p| matches!(p, ProjectionElem::Deref))
1812 {
1813 continue;
1814 }
1815 let resolved =
1816 resolve_via_tree(&tree, place.local, &PlaceKey::from_mir_place(place).fields);
1817 if resolved.0 == 1
1818 && !resolved.1.is_empty()
1819 && holder_origins
1820 .iter()
1821 .any(|(h, hf)| *h == resolved.0 && *hf == resolved.1)
1822 {
1823 return Some(
1824 "pre-existing &*raw_ptr view aliases the ownership-transferred pointer".into(),
1825 );
1826 }
1827 }
1828 }
1829 None
1830}
1831
1832fn resolve_via_tree(
1836 tree: &crate::verify::vm::alias_tree::AliasTree,
1837 local: Local,
1838 fields: &[usize],
1839) -> (usize, Vec<usize>) {
1840 if !fields.is_empty() {
1841 return (local.as_usize(), fields.to_vec());
1842 }
1843 tree.resolve_local_to_root(local)
1844}
1845
1846pub(super) fn private_fn_callsite_delegation(
1849 tcx: TyCtxt<'_>,
1850 caller: DefId,
1851 origin: &PlaceKey,
1852 kind: HazardKind,
1853) -> Option<String> {
1854 let param_index = param_index_of_origin(tcx, caller, origin)?;
1855 if is_externally_reachable(tcx, caller) {
1856 return None;
1857 }
1858 for site in local_callsites(tcx, caller) {
1859 let mut origins = callsite_arg_origins(tcx, site.caller, &site.args, param_index);
1860 if origins.is_empty() {
1861 continue;
1862 }
1863 let tree = crate::verify::vm::alias_tree::AliasTree::build(tcx, site.caller);
1864 let extra = find_as_ptr_receivers(tcx, site.caller, &origins, &tree, false);
1865 for place in extra {
1866 if !origins.contains(&place) {
1867 origins.push(place);
1868 }
1869 }
1870 if let Some(reason) = local_hazard_violation_with(
1871 tcx,
1872 site.caller,
1873 site.block,
1874 site.destination,
1875 &origins,
1876 kind,
1877 true,
1878 None,
1879 ) {
1880 return Some(format!(
1881 "call site `{}` conflicts with the returned view: {reason}",
1882 tcx.def_path_str(site.caller)
1883 ));
1884 }
1885 }
1886 None
1887}
1888
1889fn local_callsites(tcx: TyCtxt<'_>, callee: DefId) -> Vec<LocalCallsite<'_>> {
1890 let mut sites = Vec::new();
1891 for def_id in tcx.mir_keys(()) {
1892 let def_id = def_id.to_def_id();
1893 if def_id == callee {
1894 continue;
1895 }
1896 if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
1897 continue;
1898 }
1899 if !tcx.is_mir_available(def_id) {
1900 continue;
1901 }
1902 let body = tcx.optimized_mir(def_id);
1903 for (block, data) in body.basic_blocks.iter_enumerated() {
1904 let Some(terminator) = &data.terminator else {
1905 continue;
1906 };
1907 let TerminatorKind::Call {
1908 func,
1909 args,
1910 destination,
1911 ..
1912 } = &terminator.kind
1913 else {
1914 continue;
1915 };
1916 let Some(target) = crate::helpers::mir_utils::dep_callee_def_id(func) else {
1917 continue;
1918 };
1919 if target != callee {
1920 continue;
1921 }
1922 sites.push(LocalCallsite {
1923 caller: def_id,
1924 block,
1925 args: args.iter().map(|arg| arg.node.clone()).collect(),
1926 destination: Some(destination.local),
1927 });
1928 }
1929 }
1930 sites
1931}
1932
1933fn callsite_arg_origins(
1934 tcx: TyCtxt<'_>,
1935 caller: DefId,
1936 args: &[Operand<'_>],
1937 param_index: usize,
1938) -> Vec<PlaceKey> {
1939 let Some(arg) = args.get(param_index) else {
1940 return Vec::new();
1941 };
1942 let Some(place) = (match arg {
1943 Operand::Copy(place) | Operand::Move(place) => Some(PlaceKey::from_mir_place(place)),
1944 _ => None,
1945 }) else {
1946 return Vec::new();
1947 };
1948 let tree = crate::verify::vm::alias_tree::AliasTree::build(tcx, caller);
1949 let mut origins = vec![place.clone()];
1950 if let Some(local) = place.local()
1951 && tree.tag_of(local).is_some()
1952 {
1953 let (root, fields) = tree.resolve_local_to_root(local);
1954 let alias = PlaceKey::from_origin(root, fields);
1955 if !origins.contains(&alias) {
1956 origins.push(alias);
1957 }
1958 }
1959 origins
1960}