1#[cfg(rapx_const_ext)]
4use rustc_middle::ty::consts::ConstExt;
5use rustc_middle::{
6 mir::{Local, Place, ProjectionElem},
7 ty::{Ty, TyKind},
8};
9use z3::{Context, Sort, ast::{Array, Ast, Bool, Int}};
10
11use super::state::{AllocId, AllocKind, Allocation, MemoryContent, MemoryUnit, Provenance, ValueFacts, ValueSource, VmState, VmValue};
12
13impl<'z3, 'tcx> VmState<'z3, 'tcx> {
14 pub(crate) fn address_of_place(&mut self, place: &Place<'tcx>) -> Option<VmValue<'z3, 'tcx>> {
15 self.ensure_local_allocation(place.local);
16
17 let zero = Int::from_u64(self.z3_ctx, 0);
18
19 if place.projection.is_empty() {
20 let base_addr = self.local_address(place.local);
21 let ty = self.body().local_decls[place.local].ty;
22 let provenance = self
26 .local_value(place.local)
27 .and_then(|v| v.provenance.clone())
28 .or_else(|| {
29 self.current_frame.local_alloc
30 .get(&place.local)
31 .copied()
32 .map(|alloc_id| Provenance {
33 alloc_id,
34 offset: zero,
35 offset_kind: None,
36 })
37 });
38 return Some(VmValue {
39 z3_term: base_addr,
40 ty,
41 provenance,
42 facts: ValueFacts::default(),
43 source: ValueSource::None,
44 });
45 }
46
47 let mut term = self.local_address(place.local);
48 let mut provenance: Option<Provenance<'z3>> = self
49 .current_frame.local_alloc
50 .get(&place.local)
51 .copied()
52 .map(|alloc_id| Provenance {
53 alloc_id,
54 offset: zero.clone(),
55 offset_kind: None,
56 });
57 let mut current_ty = self.body().local_decls[place.local].ty;
58 let mut field_path: Vec<usize> = Vec::new();
59 let mut view_ty = current_ty;
60
61 for proj in place.projection.iter() {
62 let mut handled = false;
63 if let ProjectionElem::Index(local) = proj {
64 let elem_ty = match current_ty.kind() {
70 TyKind::Array(e, _) | TyKind::Slice(e) => *e,
71 _ => current_ty,
72 };
73 let elem_sz = Int::from_u64(self.z3_ctx, self.size_of_ty(elem_ty).max(1));
74 if let Some(val) = self.local_value(local) {
75 if let Some(idx) = val.z3_term.simplify().as_u64() {
76 let scaled = Int::mul(self.z3_ctx, &[&Int::from_u64(self.z3_ctx, idx), &elem_sz]);
77 term = Int::add(self.z3_ctx, &[&term, &scaled]);
78 if let Some(ref mut prov) = provenance {
79 prov.offset = Int::add(self.z3_ctx, &[&prov.offset, &scaled]);
80 }
81 handled = true;
82 }
83 }
84 if !handled {
85 let idx = self.fresh_int("idx");
86 let scaled = Int::mul(self.z3_ctx, &[&idx, &elem_sz]);
87 term = Int::add(self.z3_ctx, &[&term, &scaled]);
88 if let Some(ref mut prov) = provenance {
89 prov.offset = Int::add(self.z3_ctx, &[&prov.offset, &scaled]);
90 }
91 }
92 continue;
93 }
94 match proj.kind() {
95 ProjectionElem::Field(field_idx, _) => {
96 let fidx = field_idx.as_usize();
97 field_path.push(fidx);
98 let field_offset = self.field_offset_in_bytes(current_ty, fidx);
99 let field_off = Int::from_u64(self.z3_ctx, field_offset);
100 let field_ty = match current_ty.kind() {
103 TyKind::Adt(adt_def, substs) => {
104 let variant = adt_def.non_enum_variant();
105 variant
106 .fields
107 .get(rustc_abi::FieldIdx::from_usize(fidx))
108 .map(|f| crate::helpers::mir_utils::field_ty(self.tcx, f, substs))
109 .unwrap_or(current_ty)
110 }
111 _ => current_ty,
112 };
113 let field_replacement = match field_ty.kind() {
119 TyKind::Slice(_) => self
120 .field_value(place.local, &field_path)
121 .and_then(|fv| fv.provenance.clone().map(|p| (fv.z3_term.clone(), p))),
122 TyKind::Array(..) => {
123 let alloc = provenance.as_ref().map(|p| p.alloc_id);
129 alloc.and_then(|a| {
130 self.units[a.0].content.values
131 .get(&(view_ty, field_path.clone()))
132 .and_then(|fv| {
133 fv.provenance.clone().map(|p| (fv.z3_term.clone(), p))
134 })
135 })
136 }
137 _ => None,
138 };
139 match field_replacement {
140 Some((fv_term, fv_prov)) => {
141 term = fv_term;
142 provenance = Some(fv_prov);
143 }
144 None => {
145 term = Int::add(self.z3_ctx, &[&term, &field_off]);
146 if let Some(ref mut prov) = provenance {
147 prov.offset = Int::add(self.z3_ctx, &[&prov.offset, &field_off]);
148 }
149 }
150 }
151 current_ty = field_ty;
152 }
153 ProjectionElem::Deref => {
154 field_path.clear();
155 let pointed = self.local_value(place.local)?;
156 term = pointed.z3_term.clone();
157 provenance = pointed.provenance.clone();
158 if provenance.is_none() && matches!(pointed.ty.kind(), TyKind::RawPtr(..)) {
161 if let Some(field0) = self.field_value(place.local, &[0]) {
162 provenance = field0.provenance.clone();
163 }
164 }
165 if let TyKind::Ref(_, deref_ty, _) = current_ty.kind() {
166 current_ty = *deref_ty;
167 } else if let TyKind::RawPtr(deref_ty, _) = current_ty.kind() {
168 current_ty = *deref_ty;
169 }
170 view_ty = current_ty;
171 }
172 _ => {
173 return None;
174 }
175 }
176 }
177
178 let ty = place.ty(self.body(), self.tcx).ty;
179 Some(VmValue {
180 z3_term: term,
181 ty,
182 provenance,
183 facts: ValueFacts::default(),
184 source: ValueSource::None,
185 })
186 }
187
188 pub(crate) fn ensure_local_allocation(&mut self, local: Local) {
190 if self.current_frame.local_alloc.contains_key(&local) {
191 return;
192 }
193 let ty = self.body().local_decls[local].ty;
194 let align = self.align_sym(ty);
195 let name = format!("addr__{}", local.as_usize());
198 let base = Int::new_const(self.z3_ctx, name.as_str());
199 let id = AllocId(self.units.len());
200 let (size_term, element_ty, slice_len) = match ty.kind() {
208 TyKind::Array(elem, const_len) => {
209 let elem_size = self.size_of_ty(*elem).max(1);
217 let n_term = self.const_len_term(const_len);
218 let size = match n_term.as_u64() {
219 Some(n) => Int::from_u64(self.z3_ctx, n.saturating_mul(elem_size)),
220 None => Int::mul(self.z3_ctx, &[&n_term, &Int::from_u64(self.z3_ctx, elem_size)]),
221 };
222 (size, Some(*elem), Some(n_term))
223 }
224 _ => {
225 let size = self
226 .struct_size_sym(ty)
227 .unwrap_or_else(|| self.size_sym(ty));
228 (size, Some(ty), None)
229 }
230 };
231 let mut alloc = Allocation::new(base, size_term, align, element_ty, AllocKind::Object);
232 if let Some(len) = slice_len {
233 alloc.set_slice_len(len);
234 }
235 self.units.push(MemoryUnit {
236 allocation: alloc,
237 content: MemoryContent::default(),
238 });
239 self.current_frame.local_alloc.insert(local, id);
240 }
241
242 pub(crate) fn field_offset_in_bytes(&self, ty: Ty<'tcx>, field_idx: usize) -> u64 {
243 crate::helpers::mir_utils::field_offset_in_bytes(
244 self.tcx,
245 self.current_frame.current_def_id,
246 ty,
247 field_idx,
248 )
249 }
250
251 pub(crate) fn size_of_ty(&self, ty: Ty<'tcx>) -> u64 {
252 crate::helpers::mir_utils::layout_of_ty(self.tcx, self.current_frame.current_def_id, ty)
253 .map(|l| l.size.bytes())
254 .unwrap_or(0)
255 }
256
257 pub(crate) fn align_of_ty(&self, ty: Ty<'tcx>) -> u64 {
258 crate::helpers::mir_utils::layout_of_ty(self.tcx, self.current_frame.current_def_id, ty)
259 .map(|l| l.align.abi.bytes())
260 .unwrap_or(1)
261 }
262
263 pub(crate) fn allocation_size(&self, alloc_id: AllocId) -> &Int<'z3> {
264 &self.alloc(alloc_id).size
265 }
266
267 pub(crate) fn allocation_base(&self, alloc_id: AllocId) -> &Int<'z3> {
268 &self.alloc(alloc_id).base
269 }
270
271 pub(crate) fn pointee_elem_size(&self, ty: Ty<'tcx>) -> u64 {
274 let inner = match ty.kind() {
275 TyKind::RawPtr(inner_ty, _) | TyKind::Ref(_, inner_ty, _) => *inner_ty,
276 _ => ty,
277 };
278 match inner.kind() {
279 TyKind::Slice(elem) => self.size_of_ty(*elem),
280 _ => self.size_of_ty(inner),
281 }
282 }
283
284 pub(crate) fn size_sym(&mut self, ty: Ty<'tcx>) -> Int<'z3> {
291 let ty = peel_slice_elem(ty);
292 let size = self.size_of_ty(ty);
293 if size > 0 || !crate::helpers::mir_utils::ty_has_type_param(ty) {
294 return Int::from_u64(self.z3_ctx, size);
295 }
296 if let Some(s) = self.constraints.term_caches.sizes.get(&ty) {
297 return s.clone();
298 }
299 let s = self.fresh_int(&format!("sizeof_{ty}"));
300 self.constraints.term_caches.sizes.insert(ty, s.clone());
301 let zero = Int::from_u64(self.z3_ctx, 0);
302 self.constraints.assertions.push(s.ge(&zero));
303 s
304 }
305
306 fn const_len_term(&self, const_len: &rustc_middle::ty::Const<'tcx>) -> Int<'z3> {
310 match const_len.try_to_target_usize(self.tcx) {
311 Some(v) => Int::from_u64(self.z3_ctx, v),
312 None => {
313 let const_text = format!("Ty({:?}, {:?})", self.tcx.types.usize, const_len);
314 let name = format!("const_{}", const_text.replace([':', '#', ' '], "_"));
315 Int::new_const(self.z3_ctx, name.as_str())
316 }
317 }
318 }
319
320 pub(crate) fn size_sym_read(&self, ty: Ty<'tcx>) -> Int<'z3> {
327 let ty = peel_slice_elem(ty);
328 let size = self.size_of_ty(ty);
329 if size > 0 {
330 return Int::from_u64(self.z3_ctx, size);
331 }
332 self.constraints.term_caches.sizes
333 .get(&ty)
334 .cloned()
335 .unwrap_or_else(|| Int::from_u64(self.z3_ctx, 1))
336 }
337
338 pub(crate) fn generic_elem_size(&self, alloc_id: AllocId) -> Option<Int<'z3>> {
343 let elem_ty = self.alloc(alloc_id).element_ty.as_ty()?;
344 if !crate::helpers::mir_utils::ty_has_type_param(elem_ty) {
345 return None;
346 }
347 let s = self.size_sym_read(elem_ty);
348 if s.simplify().as_u64().is_some() {
349 return None;
350 }
351 Some(s)
352 }
353
354 pub(crate) fn align_sym(&mut self, ty: Ty<'tcx>) -> Int<'z3> {
363 let ty = peel_slice_elem(ty);
364 if let TyKind::Array(elem, _) = ty.kind() {
366 return self.align_sym(*elem);
367 }
368 let align = self.align_of_ty(ty);
369 if align > 1 || !crate::helpers::mir_utils::ty_has_type_param(ty) {
370 return Int::from_u64(self.z3_ctx, align);
371 }
372 if let Some(a) = self.constraints.term_caches.aligns.get(&ty) {
373 return a.clone();
374 }
375 let a = self.fresh_int(&format!("align_{ty}"));
376 self.constraints.term_caches.aligns.insert(ty, a.clone());
377 let one = Int::from_u64(self.z3_ctx, 1);
378 let zero = Int::from_u64(self.z3_ctx, 0);
379 self.constraints.assertions.push(a.ge(&one));
380 let min_a =
383 crate::helpers::mir_utils::min_align_of_generic_param(self.tcx, self.current_frame.current_def_id, ty);
384 if min_a > 1 {
385 self.constraints.assertions
386 .push(a.ge(&Int::from_u64(self.z3_ctx, min_a)));
387 }
388 let max_a =
392 crate::helpers::mir_utils::max_align_of_generic_param(self.tcx, self.current_frame.current_def_id, ty);
393 if max_a > 0 {
394 self.constraints.assertions
395 .push(a.le(&Int::from_u64(self.z3_ctx, max_a)));
396 }
397 if let TyKind::Adt(adt_def, substs) = ty.kind() {
401 if !adt_def.is_enum() {
402 let variant = adt_def.non_enum_variant();
403 for field in variant.fields.iter() {
404 let field_ty = crate::helpers::mir_utils::field_ty(self.tcx, field, substs);
405 let field_align = self.align_sym(field_ty);
406 self.constraints.assertions.push(a.rem(&field_align)._eq(&zero));
407 }
408 if let Some(sum) = self.struct_size_sym(ty) {
415 let size = self.size_sym(ty);
416 self.constraints.assertions.push(size.ge(&sum));
417 }
418 }
419 }
420 let size = self.size_sym(ty);
422 self.constraints.assertions.push(size.rem(&a)._eq(&zero));
423 a
424 }
425
426 pub(crate) fn align_sym_read(&self, ty: Ty<'tcx>) -> Int<'z3> {
432 let ty = peel_slice_elem(ty);
433 if let TyKind::Array(elem, _) = ty.kind() {
435 return self.align_sym_read(*elem);
436 }
437 let align = self.align_of_ty(ty);
438 if align > 1 {
439 return Int::from_u64(self.z3_ctx, align);
440 }
441 if let Some(a) = self.constraints.term_caches.aligns.get(&ty) {
442 return a.clone();
443 }
444 let min_a =
445 crate::helpers::mir_utils::min_align_of_generic_param(self.tcx, self.current_frame.current_def_id, ty);
446 Int::from_u64(self.z3_ctx, min_a.max(1))
447 }
448
449 pub(crate) fn struct_size_sym(&mut self, ty: Ty<'tcx>) -> Option<Int<'z3>> {
455 let TyKind::Adt(adt_def, substs) = ty.kind() else {
456 return None;
457 };
458 if adt_def.is_enum() {
459 return None;
460 }
461 let concrete = self.size_of_ty(ty);
462 if concrete > 0 {
463 return Some(Int::from_u64(self.z3_ctx, concrete));
464 }
465 let variant = adt_def.non_enum_variant();
466 let mut total = Int::from_u64(self.z3_ctx, 0);
467 for field in variant.fields.iter() {
468 let field_ty = crate::helpers::mir_utils::field_ty(self.tcx, field, substs);
469 let field_size = self
470 .struct_size_sym(field_ty)
471 .unwrap_or_else(|| self.size_sym(field_ty));
472 total = Int::add(self.z3_ctx, &[&total, &field_size]);
473 }
474 Some(total)
475 }
476
477 fn uninit_byte(&self) -> Int<'z3> {
481 self.constraints
482 .term_caches
483 .uninit_byte
484 .clone()
485 .expect("uninit_byte is initialized in VmState::new")
486 }
487
488 fn fresh_byte_array(&self) -> Array<'z3> {
490 Array::const_array(self.z3_ctx, &Sort::int(self.z3_ctx), &self.uninit_byte())
493 }
494
495 pub(crate) fn byte_read(&self, alloc_id: AllocId, i: &Int<'z3>) -> Int<'z3> {
502 match &self.units[alloc_id.0].content.byte_array {
503 Some(arr) => arr.select(i).as_int().expect("byte array range is Int").simplify(),
504 None => self.uninit_byte(),
505 }
506 }
507
508 pub(crate) fn byte_write(&mut self, alloc_id: AllocId, i: &Int<'z3>, v: &Int<'z3>) {
510 let arr = self.units[alloc_id.0].content.byte_array.clone();
511 let updated = match arr {
512 Some(a) => a.store(i, v),
513 None => self.fresh_byte_array().store(i, v),
514 };
515 let unit = &mut self.units[alloc_id.0];
516 unit.content.byte_array = Some(updated);
517 if let Some(off) = i.as_u64() {
518 unit.content.byte_written.insert(off as usize);
519 }
520 }
521
522 pub(crate) fn record_byte_value(&mut self, alloc_id: AllocId, offset: usize, term: Int<'z3>) {
524 self.byte_write(alloc_id, &Int::from_u64(self.z3_ctx, offset as u64), &term);
525 }
526
527 pub(crate) fn mark_byte_init(&mut self, alloc_id: AllocId, offset: usize) {
529 let unknown = self.fresh_int("byte_unknown");
530 self.byte_write(alloc_id, &Int::from_u64(self.z3_ctx, offset as u64), &unknown);
531 }
532
533 pub(crate) fn is_byte_init(&self, alloc_id: AllocId, offset: usize) -> bool {
535 let v = self.byte_read(alloc_id, &Int::from_u64(self.z3_ctx, offset as u64));
536 !v.simplify().eq(&self.uninit_byte())
537 }
538
539 pub(crate) fn is_byte_nul(&self, alloc_id: AllocId, offset: usize) -> bool {
541 self.byte_read(alloc_id, &Int::from_u64(self.z3_ctx, offset as u64))
542 .simplify()
543 .as_u64()
544 == Some(0)
545 }
546
547 pub(crate) fn is_byte_non_nul(&self, alloc_id: AllocId, offset: usize) -> bool {
549 self.byte_read(alloc_id, &Int::from_u64(self.z3_ctx, offset as u64))
550 .simplify()
551 .as_u64()
552 .is_some_and(|v| v != 0)
553 }
554
555 pub(crate) fn alloc_byte_values(&self, alloc_id: AllocId) -> Vec<(usize, Int<'z3>)> {
558 let mut offs: Vec<usize> = self.units[alloc_id.0]
559 .content
560 .byte_written
561 .iter()
562 .copied()
563 .collect();
564 offs.sort_unstable();
565 offs.into_iter()
566 .map(|off| (off, self.byte_read(alloc_id, &Int::from_u64(self.z3_ctx, off as u64))))
567 .collect()
568 }
569
570 pub(crate) fn alloc_nul_offsets(&self, alloc_id: AllocId) -> Vec<usize> {
572 let mut offs: Vec<usize> = self.units[alloc_id.0]
573 .content
574 .byte_written
575 .iter()
576 .copied()
577 .filter(|&off| self.is_byte_nul(alloc_id, off))
578 .collect();
579 offs.sort_unstable();
580 offs
581 }
582
583 pub(crate) fn alloc_non_nul_offsets(&self, alloc_id: AllocId) -> Vec<usize> {
585 let mut offs: Vec<usize> = self.units[alloc_id.0]
586 .content
587 .byte_written
588 .iter()
589 .copied()
590 .filter(|&off| self.is_byte_non_nul(alloc_id, off))
591 .collect();
592 offs.sort_unstable();
593 offs
594 }
595
596 pub(crate) fn copy_byte_tracking(&mut self, src: AllocId, src_offset: usize, dst: AllocId) {
599 let written: Vec<usize> = self.units[src.0]
600 .content
601 .byte_written
602 .iter()
603 .copied()
604 .filter(|&off| off >= src_offset)
605 .collect();
606 for off in written {
607 let v = self.byte_read(src, &Int::from_u64(self.z3_ctx, off as u64));
608 self.byte_write(dst, &Int::from_u64(self.z3_ctx, (off - src_offset) as u64), &v);
609 }
610 }
611
612 pub(crate) fn load_value(
623 &self,
624 alloc_id: AllocId,
625 view_ty: Ty<'tcx>,
626 path: &[usize],
627 ) -> Option<&VmValue<'z3, 'tcx>> {
628 self.units[alloc_id.0]
629 .content
630 .values
631 .get(&(view_ty, path.to_vec()))
632 }
633
634 pub(crate) fn store_value(
640 &mut self,
641 alloc_id: AllocId,
642 view_ty: Ty<'tcx>,
643 path: Vec<usize>,
644 value: VmValue<'z3, 'tcx>,
645 ) {
646 self.units[alloc_id.0]
647 .content
648 .values
649 .insert((view_ty, path), value);
650 }
651
652 pub(crate) fn utf8_validity(&self, alloc_id: AllocId) -> Option<Bool<'z3>> {
656 let byte_pairs = self.alloc_byte_values(alloc_id);
657 if byte_pairs.is_empty() {
658 return None;
659 }
660 let bytes: Vec<Int<'z3>> = byte_pairs.into_iter().map(|(_, t)| t).collect();
661 Some(utf8_validity_dfa(self.z3_ctx, &bytes))
662 }
663}
664
665fn utf8_validity_dfa<'z3>(z3_ctx: &'z3 Context, bytes: &[Int<'z3>]) -> Bool<'z3> {
673 let zero = Int::from_u64(z3_ctx, 0);
674 let one = Int::from_u64(z3_ctx, 1);
675 let two = Int::from_u64(z3_ctx, 2);
676 let three = Int::from_u64(z3_ctx, 3);
677
678 let c_0x80 = Int::from_u64(z3_ctx, 0x80);
679 let c_0xc0 = Int::from_u64(z3_ctx, 0xC0);
680 let c_0xc2 = Int::from_u64(z3_ctx, 0xC2);
681 let c_0xe0 = Int::from_u64(z3_ctx, 0xE0);
682 let c_0xf0 = Int::from_u64(z3_ctx, 0xF0);
683 let c_0xf5 = Int::from_u64(z3_ctx, 0xF5);
684 let c_0xa0 = Int::from_u64(z3_ctx, 0xA0);
685 let c_0x90 = Int::from_u64(z3_ctx, 0x90);
686 let c_0xed = Int::from_u64(z3_ctx, 0xED);
687 let c_0xf4 = Int::from_u64(z3_ctx, 0xF4);
688
689 let mut valid = Bool::from_bool(z3_ctx, true);
690 let mut state = zero.clone();
691 let mut lead = zero.clone();
692
693 for b in bytes {
694 let is_ascii = b.lt(&c_0x80);
695 let is_cont = b.ge(&c_0x80) & b.lt(&c_0xc0);
696 let is_2lead = b.ge(&c_0xc2) & b.lt(&c_0xe0);
697 let is_3lead = b.ge(&c_0xe0) & b.lt(&c_0xf0);
698 let is_4lead = b.ge(&c_0xf0) & b.lt(&c_0xf5);
699
700 let refine_3 =
701 (lead._eq(&c_0xe0).not() | b.ge(&c_0xa0)) & (lead._eq(&c_0xed).not() | b.lt(&c_0xa0));
702 let refine_4 =
703 (lead._eq(&c_0xf0).not() | b.ge(&c_0x90)) & (lead._eq(&c_0xf4).not() | b.lt(&c_0x90));
704
705 let valid_s0 = is_ascii.clone() | is_2lead.clone() | is_3lead.clone() | is_4lead.clone();
706 let valid_s1 = is_cont.clone();
707 let valid_s2 = is_cont.clone() & refine_3;
708 let valid_s3 = is_cont.clone() & refine_4;
709
710 let state0 = state._eq(&zero);
711 let state1 = state._eq(&one);
712 let state2 = state._eq(&two);
713
714 let byte_valid = Bool::ite(
715 &state0,
716 &valid_s0,
717 &Bool::ite(
718 &state1,
719 &valid_s1,
720 &Bool::ite(&state2, &valid_s2, &valid_s3),
721 ),
722 );
723
724 let new_state_s0 = Bool::ite(
725 &is_ascii,
726 &zero,
727 &Bool::ite(&is_2lead, &one, &Bool::ite(&is_3lead, &two, &three)),
728 );
729 let new_state_cont = Bool::ite(&state1, &zero, &Bool::ite(&state2, &one, &two));
730 let new_state = Bool::ite(&state0, &new_state_s0, &new_state_cont);
731
732 valid = valid & byte_valid;
733 let is_lead34 = is_3lead | is_4lead;
734 lead = Bool::ite(&(state0 & is_lead34), b, &lead);
735 state = new_state;
736 }
737
738 valid = valid & state._eq(&zero);
739 valid
740}
741
742fn peel_slice_elem(ty: Ty<'_>) -> Ty<'_> {
744 match ty.kind() {
745 TyKind::Slice(elem) => *elem,
746 _ => ty,
747 }
748}