Skip to main content

vyre_libs/security/
integer_overflow_arith.rs

1//! `integer_overflow_arith`  -  does this binary op overflow on
2//! attacker input? CWE-190 supporting predicate.
3//!
4//! Per node `n`, write 1 iff `n` is a binary arithmetic node
5//! (mul / add / shl) AND at least one operand is reachable from
6//! `@http_input_family` AND there is no dominating overflow check.
7
8use vyre_foundation::ir::Program;
9use vyre_primitives::bitset::and::bitset_and;
10use vyre_primitives::bitset::and_not::bitset_and_not;
11use vyre_primitives::bitset::bitset_words;
12
13use crate::security::flow_composition::fuse_security_flow;
14
15pub(crate) const OP_ID: &str = "vyre-libs::security::integer_overflow_arith";
16
17/// Build an overflow-check Program: `arith_set AND attacker_reach`
18/// lands in `intermediate`, then that set minus
19/// `overflow_check_dominates` lands in `out`.
20#[must_use]
21pub fn integer_overflow_arith(
22    node_count: u32,
23    arith_set: &str,
24    attacker_reach: &str,
25    overflow_check_dominates: &str,
26    intermediate: &str,
27    out: &str,
28) -> Program {
29    let words = bitset_words(node_count);
30    fuse_security_flow(
31        OP_ID,
32        &[
33            bitset_and(arith_set, attacker_reach, intermediate, words),
34            bitset_and_not(intermediate, overflow_check_dominates, out, words),
35        ],
36        out,
37    )
38}
39
40/// CPU oracle.
41#[must_use]
42#[cfg(test)]
43pub(crate) fn cpu_ref(
44    arith_set: &[u32],
45    attacker_reach: &[u32],
46    overflow_check_dominates: &[u32],
47) -> Vec<u32> {
48    let inter = vyre_primitives::bitset::and::cpu_ref(arith_set, attacker_reach);
49    vyre_primitives::bitset::and_not::cpu_ref(&inter, overflow_check_dominates)
50}
51
52/// Soundness marker for [`integer_overflow_arith`].
53pub struct IntegerOverflowArith;
54impl vyre_spec::soundness::SoundnessTagged for IntegerOverflowArith {
55    fn soundness(&self) -> vyre_spec::soundness::Soundness {
56        vyre_spec::soundness::Soundness::Exact
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn unguarded_attacker_arith_fires() {
66        // arith {0,1,2,3}, attacker {1,2}, no checks.
67        assert_eq!(cpu_ref(&[0b1111], &[0b0110], &[0]), vec![0b0110]);
68    }
69
70    #[test]
71    fn guarded_does_not_fire() {
72        assert_eq!(cpu_ref(&[0b1111], &[0b0110], &[0b0010]), vec![0b0100]);
73    }
74
75    #[test]
76    fn no_attacker_means_no_finding() {
77        assert_eq!(cpu_ref(&[0b1111], &[0], &[0]), vec![0]);
78    }
79
80    #[test]
81    fn no_arith_means_no_finding() {
82        assert_eq!(cpu_ref(&[0], &[0xFFFF], &[0]), vec![0]);
83    }
84}