Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(c2rust-analyze) Support ptr-to-ptr casts between safely transmutable types, for now limited to same-sized integers #839

Merged
merged 35 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
81d4e46
(`c2rust-analyze`) Support ptr-to-ptr casts between safely transmutab…
kkysen Feb 16, 2023
68461db
(`c2rust-analyze`) Clarified that `do_unify` now requires only compat…
kkysen Feb 16, 2023
da4d961
(`c2rust-analyze`) Support deeper levels of ptr transmutability (e.x.…
kkysen Feb 17, 2023
62ec8dc
(`c2rust-analyze`) Formally defined the safe transmutability definition.
kkysen Feb 17, 2023
0d88d7a
(`c2rust-analyze`) Relaxed the transmutable checks from two-way to on…
kkysen Feb 16, 2023
a148146
(`c2rust-analyze`) Updated `do_unify` docs after the renaming.
kkysen Feb 16, 2023
26a4275
(`c2rust-analyze`) Fix the `is_transmutable_to` docs, formalizing the…
kkysen Feb 18, 2023
eae9234
(`c2rust-analyze`) Relax the transmutable checks from two-way to one-…
kkysen Apr 26, 2023
fe926ea
Merge branch 'master' into kkysen/analyze-string-casts
kkysen May 1, 2023
182b0b5
(`c2rust-analyze`) Expand transmutability to unsizing casts (`[A] => …
kkysen May 1, 2023
32ca464
(`c2rust-analyze/tests`) Enable the `cast_from_literal` test now that…
kkysen May 1, 2023
9a7c501
(`c2rust-analyze`) Revert the use of `is_transmutable_to` in `TypeChe…
kkysen May 1, 2023
0c9d0ac
Merge branch 'master' into kkysen/analyze-string-casts
kkysen May 1, 2023
2d45f80
(`c2rust-analyze`) Add back the unsizing cast dataflow constraint fro…
kkysen May 1, 2023
14824a1
(`c2rust-analyze/tests`) Disable the failing string casts tests.
kkysen May 2, 2023
95767c3
(`c2rust-analyze/tests`) Add an explicit (in terms of `addr_of!`) ver…
kkysen May 2, 2023
e41cec1
(`c2rust-analyze/tests`) Add an explicit version of the `cast_from_li…
kkysen May 2, 2023
2915b8d
(`c2rust-analyze/tests`) Add a disabled `cast_array_to_slice_ptr_expl…
kkysen May 2, 2023
11bf351
(`c2rust-analyze/test`) Reword explanation of explicit string cast te…
kkysen May 2, 2023
65e5140
(`c2rust-analyze`) Remove the leading `|` in a `matches!` so `rustfmt…
kkysen May 5, 2023
44ac9f4
(`c2rust-analyze`) Replace "equivalance relation" with "reflexive, tr…
kkysen May 5, 2023
4bbb306
(`c2rust-analyze`) Separate handling of `CastKind`s and only check sa…
kkysen May 22, 2023
720c2fb
Revert "(`c2rust-analyze`) Expand transmutability to unsizing casts (…
kkysen May 22, 2023
df1d63d
(`c2rust-analyze/tests`) Remove the `cast_array_to_slice_ptr` tests a…
kkysen May 22, 2023
b15e24e
Merge branch 'master' into kkysen/analyze-string-casts
kkysen May 25, 2023
6ea8a7e
Merge branch 'master' into kkysen/analyze-string-casts
kkysen Jun 5, 2023
1fd669c
Merge branch 'master' into kkysen/analyze-string-casts
kkysen Jun 5, 2023
a73e64d
(`c2rust-analyze`) Adjust wording on safe transmutability definition …
kkysen Jun 8, 2023
435e399
(`c2rust-analyze`) Revert the arg names of `do_unify` to `lty{1,2}` f…
kkysen Jun 8, 2023
1317e03
(`c2rust-analyze`) Update safe transmutability rules to add `A ~ B =>…
kkysen Jun 8, 2023
60a4376
(`c2rust-analyze`) For the array safe transmutability rule, require t…
kkysen Jun 8, 2023
9b43bd6
(`c2rust-analyze`) Remove the slice rule for safe transmutability, as…
kkysen Jun 8, 2023
355b56b
(`c2rust-analyze`) `.unwrap()` on `.try_to_scalar_int()` for array le…
kkysen Jun 9, 2023
7f87dc1
(`c2rust-analyze`) Add fat ptr analysis/proof to safe transmutability…
kkysen Jun 9, 2023
31baf0a
Merge branch 'master' into kkysen/analyze-string-casts
kkysen Jun 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 16 additions & 17 deletions c2rust-analyze/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::pointer_id::{
GlobalPointerTable, LocalPointerTable, NextGlobalPointerId, NextLocalPointerId, PointerTable,
PointerTableMut,
};
use crate::util::{self, describe_rvalue, RvalueDesc};
use crate::util::{self, are_transmutable_ptrs, describe_rvalue, RvalueDesc};
use crate::AssignPointerIds;
use bitflags::bitflags;
use rustc_hir::def_id::DefId;
Expand Down Expand Up @@ -349,23 +349,22 @@ impl<'a, 'tcx> AnalysisCtxt<'a, 'tcx> {
Rvalue::Cast(_, ref op, ty) => {
let op_lty = self.type_of(op);

// We support this category of pointer casts as a special case.
let op_is_ptr = matches!(op_lty.ty.kind(), TyKind::Ref(..) | TyKind::RawPtr(..));
let op_pointee = op_is_ptr.then(|| op_lty.args[0]);
let ty_pointee = match *ty.kind() {
TyKind::Ref(_, ty, _) => Some(ty),
TyKind::RawPtr(tm) => Some(tm.ty),
_ => None,
};
if op_pointee.is_some() && op_pointee.map(|lty| lty.ty) == ty_pointee {
// The source and target types are both pointers, and they have identical
// pointee types. We label the target type with the same `PointerId`s as the
// source type in all positions. This works because the two types have the
// same structure.
return self.lcx().mk(ty, op_lty.args, op_lty.label);
// We only support pointer casts when:
// * both types are pointers
// * they have compatible (safely transmutable) pointee types
// Safe transmutability is difficult to check abstractly,
// so we limit it to integer types of the same size
// (but potentially different signedness).
// In particular, this allows casts from `*u8` to `*core::ffi::c_char`.
let from_ty = op_lty.ty;
let to_ty = ty;
match are_transmutable_ptrs(from_ty, to_ty) {
// Label the to type with the same [`PointerId`]s as the from type in all positions.
// This works because the two types have the same structure.
Some(true) => self.lcx().mk(ty, op_lty.args, op_lty.label),
Some(false) => todo!("unsupported ptr-to-ptr cast between pointee types not yet supported as safely transmutable: `{from_ty:?} as {to_ty:?}`"),
None => label_no_pointers(self, ty),
}

label_no_pointers(self, ty)
}
Rvalue::Len(..)
| Rvalue::BinaryOp(..)
Expand Down
17 changes: 10 additions & 7 deletions c2rust-analyze/src/dataflow/type_check.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::DataflowConstraints;
use crate::context::{AnalysisCtxt, LTy, PermissionSet, PointerId};
use crate::util::{self, describe_rvalue, Callee, RvalueDesc};
use crate::util::{self, are_transmutable, describe_rvalue, Callee, RvalueDesc};
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
AggregateKind, BinOp, Body, Location, Mutability, Operand, Place, PlaceRef, ProjectionElem,
Expand Down Expand Up @@ -202,15 +202,18 @@ impl<'tcx> TypeChecker<'tcx, '_> {

/// Unify corresponding `PointerId`s in `lty1` and `lty2`.
///
/// The two inputs must have identical underlying types. For any position where the underlying
/// type has a pointer, this function unifies the `PointerId`s that `lty1` and `lty2` have at
/// The two inputs must have compatible ([safely transmutable](are_transmutable)) underlying types.
/// For any position where the underlying type has a pointer,
/// this function unifies the `PointerId`s that `lty1` and `lty2` have at
/// that position. For example, given `lty1 = *mut /*l1*/ *const /*l2*/ u8` and `lty2 = *mut
/// /*l3*/ *const /*l4*/ u8`, this function will unify `l1` with `l3` and `l2` with `l4`.
kkysen marked this conversation as resolved.
Show resolved Hide resolved
fn do_unify(&mut self, lty1: LTy<'tcx>, lty2: LTy<'tcx>) {
assert_eq!(
self.acx.tcx().erase_regions(lty1.ty),
self.acx.tcx().erase_regions(lty2.ty)
);
let ty1 = lty1.ty;
let ty2 = lty2.ty;
assert!(are_transmutable(
self.acx.tcx().erase_regions(ty1),
self.acx.tcx().erase_regions(ty2),
), "types not transmutable (compatible), so PointerId unification cannot be done: {ty1:?} !~ {ty2:?}");
kkysen marked this conversation as resolved.
Show resolved Hide resolved
for (sub_lty1, sub_lty2) in lty1.iter().zip(lty2.iter()) {
eprintln!("equate {:?} = {:?}", sub_lty1, sub_lty2);
if sub_lty1.label != PointerId::NONE || sub_lty2.label != PointerId::NONE {
Expand Down
57 changes: 56 additions & 1 deletion c2rust-analyze/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
Field, Local, Mutability, Operand, PlaceElem, PlaceRef, ProjectionElem, Rvalue,
};
use rustc_middle::ty::{AdtDef, DefIdTree, SubstsRef, Ty, TyCtxt, TyKind, UintTy};
use rustc_middle::ty::{self, AdtDef, DefIdTree, SubstsRef, Ty, TyCtxt, TyKind, UintTy};
use rustc_type_ir::IntTy;
use std::fmt::Debug;

#[derive(Debug)]
Expand Down Expand Up @@ -305,3 +306,57 @@ pub fn lty_project<'tcx, L: Debug>(
ProjectionElem::Downcast(..) => todo!("type_of Downcast"),
}
}

/// Determine if two types are safe to transmute to each other.
///
/// Safe transmutability is difficult to check abstractly,
/// so here it is limited to integer types of the same size
/// (but potentially different signedness).
///
/// Extra (but equal) levels of pointer/reference indirection are allowed,
/// i.e. `u8 ~ i8` implies `**u8 ~ **i8`.
///
/// Thus, [`true`] means it is definitely transmutable,
/// while [`false`] means it may not be transmutable.
///
/// Formally, safe transmutability defines
/// an equivalence relation on types, named `~` here.
/// `A ~ B` iff `*(a as *const B)` and `*(b as *const A)` are safe,
/// where `a: *const A` and `b: *const B`.
///
/// And the current incomplete implementation is defined as:
/// * `A = B => A ~ B`
/// * `A ~ B => *A ~ *B`
/// * `uN ~ iN`, where `N` is an integer width
pub fn are_transmutable<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
let transmutable_ints = || {
kkysen marked this conversation as resolved.
Show resolved Hide resolved
use IntTy::*;
use UintTy::*;
match (a.kind(), b.kind()) {
(ty::Uint(u), ty::Int(i)) | (ty::Int(i), ty::Uint(u)) => {
matches!((u, i), |(Usize, Isize)| (U8, I8)
| (U16, I16)
| (U32, I32)
| (U64, I64))
kkysen marked this conversation as resolved.
Show resolved Hide resolved
}
_ => false,
}
};

// only check for transmutable ints so far
a == b || are_transmutable_ptrs(a, b).unwrap_or(false) || transmutable_ints()
}

/// Determine if two types (e.x. in a cast) are pointers,
/// and if they are, if the pointee types are compatible,
/// i.e. they are safely transmutable to each other.
///
/// This returns [`Some`]`(is_transmutable)` if they're both pointers,
/// and [`None`] if its some other types.
///
/// See [`are_transmutable`] for the definition of safe transmutability.
pub fn are_transmutable_ptrs<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> Option<bool> {
let a = a.builtin_deref(true)?.ty;
aneksteind marked this conversation as resolved.
Show resolved Hide resolved
let b = b.builtin_deref(true)?.ty;
Some(are_transmutable(a, b))
}
5 changes: 4 additions & 1 deletion c2rust-analyze/tests/analyze/string_casts.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#[cfg(any())]
aneksteind marked this conversation as resolved.
Show resolved Hide resolved
pub fn cast_only(s: *const u8) {
s as *const core::ffi::c_char;
}

pub fn deep_cast(x: *const *const u8) {
x as *const *const i8;
}

#[cfg(any())]
pub fn cast_from_literal() {
b"" as *const u8 as *const core::ffi::c_char;
Expand Down