1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
//! This module defines traits describing general properties of abstract domains
//! as well as several abstract domain types implementing these traits.
use crate::intermediate_representation::*;
use crate::prelude::*;
mod bitvector;
pub use bitvector::BitvectorDomain;
mod identifier;
pub use identifier::*;
mod data;
pub use data::DataDomain;
mod mem_region;
pub use mem_region::MemRegion;
mod interval;
pub use interval::{Interval, IntervalDomain};
mod bricks;
pub use bricks::{BrickDomain, BricksDomain};
mod character_inclusion;
pub use character_inclusion::{CharacterInclusionDomain, CharacterSet};
mod strings;
pub use strings::*;
mod domain_map;
pub use domain_map::*;
/// The main trait describing an abstract domain.
///
/// Each abstract domain is partially ordered.
/// Abstract domains of the same type can be merged.
pub trait AbstractDomain: Sized + Eq + Clone {
/// Returns an upper bound (with respect to the partial order on the domain)
/// for the two inputs `self` and `other`.
#[must_use]
fn merge(&self, other: &Self) -> Self;
/// Returns an upper bound (with respect to the partial order on the domain)
/// for the two inputs `self` and `other`.
///
/// Modifies `self` in-place to hold the result. This can be useful in
/// situations where it is not necessary to create a new object and more
/// efficient to modify an existing one in-place.
///
/// # Default
///
/// Calls [`AbstractDomain::merge`] on the inputs and overwrites `self` with
/// the result. Does nothing when `self` is equal to `other`.
fn merge_with(&mut self, other: &Self) -> &mut Self {
if self != other {
let new_value = self.merge(other);
*self = new_value;
}
self
}
/// Returns whether the element represents the top element (i.e. maximal with respect to the partial order) or not.
/// If a domain has no maximal element, this function should always return false.
fn is_top(&self) -> bool;
}
/// A trait for types representing values with a fixed size (in bytes).
///
/// For abstract domains, the bytesize is a parameter of the domain itself,
/// i.e. you cannot merge values of different bytesizes,
/// since they lie in different posets (one for each bytesize).
pub trait SizedDomain {
/// Return the size of the represented value in bytes.
fn bytesize(&self) -> ByteSize;
/// Return a new top element with the given bytesize.
/// The function is expected to panic if the type in question does not also implement the `HasTop` trait.
fn new_top(bytesize: ByteSize) -> Self;
}
/// An abstract domain implementing this trait has a global maximum, i.e. a *Top* element.
pub trait HasTop {
/// Return an instance of the *Top* element.
///
/// Since an abstract domain type may represent a whole family of abstract domains,
/// this function takes an instance of the domain as a parameter,
/// so it can return the *Top* element of the same family member that the provided instance belongs to.
fn top(&self) -> Self;
}
/// A trait for abstract domains that can represent values loaded into CPU register.
///
/// The domain implements all general operations used to manipulate register values.
/// The domain is parametrized by its bytesize (which represents the size of the register).
/// It has a *Top* element, which is only characterized by its bytesize.
pub trait RegisterDomain: AbstractDomain + SizedDomain + HasTop {
/// Compute the (abstract) result of a binary operation
#[must_use]
fn bin_op(&self, op: BinOpType, rhs: &Self) -> Self;
/// Compute the (abstract) result of a unary operation
#[must_use]
fn un_op(&self, op: UnOpType) -> Self;
/// Extract a sub-bitvector
#[must_use]
fn subpiece(&self, low_byte: ByteSize, size: ByteSize) -> Self;
/// Perform a typecast to extend a bitvector or to cast between integer and floating point types.
#[must_use]
fn cast(&self, kind: CastOpType, width: ByteSize) -> Self;
/// Return the bytesize of the result of the given binary operation.
/// Has a generic implementation that should not be overwritten!
fn bin_op_bytesize(&self, op: BinOpType, rhs: &Self) -> ByteSize {
use BinOpType::*;
match op {
Piece => self.bytesize() + rhs.bytesize(),
IntAdd | IntSub | IntMult | IntDiv | IntSDiv | IntRem | IntSRem | IntLeft
| IntRight | IntSRight | IntAnd | IntOr | IntXOr | FloatAdd | FloatSub | FloatMult
| FloatDiv => self.bytesize(),
IntEqual | IntNotEqual | IntLess | IntLessEqual | IntSLess | IntSLessEqual
| IntCarry | IntSCarry | IntSBorrow | BoolAnd | BoolOr | BoolXOr | FloatEqual
| FloatNotEqual | FloatLess | FloatLessEqual => ByteSize::new(1),
}
}
}
/// A conversion trait for abstract domains that can represent register values.
pub trait TryToBitvec {
/// If `self` represents a single absolute value, return it.
/// In all other cases return an error.
fn try_to_bitvec(&self) -> Result<Bitvector, Error>;
/// If `self` represents a single absolute value, try to convert it to a signed integer and return it.
/// Else return an error.
/// Note that the conversion loses information about the bytesize of the value.
fn try_to_offset(&self) -> Result<i64, Error> {
Ok(self.try_to_bitvec()?.try_to_i64()?)
}
}
/// A conversion trait for abstract domains that can represent register values.
pub trait TryToInterval {
/// If `self` represents an interval of absolute values (or can be widened to represent such an interval)
/// then return it if the interval is bounded.
/// For unbounded (i.e. `Top`) intervals or if the abstract value does not represent absolute values return an error.
fn try_to_interval(&self) -> Result<Interval, Error>;
/// If `self` represents an interval of absolute values (or can be widened to represent such an interval)
/// then return it as an interval of signed integers of the form `(start, end)` if the interval is bounded.
/// Else return an error.
/// Note that the conversion loses information about the bytesize of the values contained in the interval.
fn try_to_offset_interval(&self) -> Result<(i64, i64), Error> {
let interval = self.try_to_interval()?;
Ok((interval.start.try_to_i64()?, interval.end.try_to_i64()?))
}
}
/// A trait for domains whose values can be restricted by knowing the result of a comparison of it with a known bitvector.
/// The comparison may also be used to add widening hints to the domain.
///
/// Note that the value set represented by the domain after the restriction may be an upper bound,
/// i.e. it is possible that the result still contains values not satisfying the restricting comparison.
pub trait SpecializeByConditional: Sized {
/// Return the restriction of `self` to values satisfying `self <= bound`
/// with `self` and `bound` interpreted as signed integers.
/// Returns an error if no value represented by `self` can satisfy the comparison.
fn add_signed_less_equal_bound(self, bound: &Bitvector) -> Result<Self, Error>;
/// Return the restriction of `self` to values satisfying `self <= bound`
/// with `self` and `bound` interpreted as unsigned integers.
/// Returns an error if no value represented by `self` can satisfy the comparison.
fn add_unsigned_less_equal_bound(self, bound: &Bitvector) -> Result<Self, Error>;
/// Return the restriction of `self` to values satisfying `self >= bound`
/// with `self` and `bound` interpreted as signed integers.
/// Returns an error if no value represented by `self` can satisfy the comparison.
fn add_signed_greater_equal_bound(self, bound: &Bitvector) -> Result<Self, Error>;
/// Return the restriction of `self` to values satisfying `self >= bound`
/// with `self` and `bound` interpreted as unsigned integers.
/// Returns an error if no value represented by `self` can satisfy the comparison.
fn add_unsigned_greater_equal_bound(self, bound: &Bitvector) -> Result<Self, Error>;
/// Return the restriction of `self` to values satisfying `self != bound`
/// Returns an error if `self` only represents one value for which `self == bound` holds.
fn add_not_equal_bound(self, bound: &Bitvector) -> Result<Self, Error>;
/// Return the intersection of two values or an error if the intersection is empty.
fn intersect(self, other: &Self) -> Result<Self, Error>;
/// Remove all widening hints from `self`.
/// Necessary for cases where several sources have widening hints,
/// but only one source should contribute widening hints to the result.
fn without_widening_hints(self) -> Self;
}