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
pub(crate) trait ForceAdd: Sized {
    fn force_add(self, rhs: Self) -> Self;
}

impl ForceAdd for u8 {
    fn force_add(self, rhs: Self) -> Self {
        self.checked_add(rhs).unwrap_or_else(die)
    }
}

impl ForceAdd for i32 {
    fn force_add(self, rhs: Self) -> Self {
        self.checked_add(rhs).unwrap_or_else(die)
    }
}

impl ForceAdd for u32 {
    fn force_add(self, rhs: Self) -> Self {
        self.checked_add(rhs).unwrap_or_else(die)
    }
}

impl ForceAdd for u64 {
    fn force_add(self, rhs: Self) -> Self {
        self.checked_add(rhs).unwrap_or_else(die)
    }
}

impl ForceAdd for usize {
    fn force_add(self, rhs: Self) -> Self {
        self.checked_add(rhs).unwrap_or_else(die)
    }
}

pub(crate) trait ForceMul: Sized {
    fn force_mul(self, rhs: Self) -> Self;
}

impl ForceMul for i32 {
    fn force_mul(self, rhs: Self) -> Self {
        self.checked_mul(rhs).unwrap_or_else(die)
    }
}

impl ForceMul for i64 {
    fn force_mul(self, rhs: Self) -> Self {
        self.checked_mul(rhs).unwrap_or_else(die)
    }
}

impl ForceMul for u64 {
    fn force_mul(self, rhs: Self) -> Self {
        self.checked_mul(rhs).unwrap_or_else(die)
    }
}

pub(crate) trait ForceInto {
    fn force_into<U>(self) -> U
    where
        Self: TryInto<U>;
}

impl<T> ForceInto for T {
    fn force_into<U>(self) -> U
    where
        Self: TryInto<U>,
    {
        <Self as TryInto<U>>::try_into(self)
            .ok()
            .unwrap_or_else(die)
    }
}

// Deterministically abort on arithmetic overflow, instead of wrapping and
// continuing with invalid behavior.
//
// This is impossible or nearly impossible to hit as the arithmetic computations
// in libyaml are all related to either:
//
//  - small integer processing (ascii, hex digits)
//  - allocation sizing
//
// and the only allocations in libyaml are for fixed-sized objects and
// geometrically growing buffers with a growth factor of 2. So in order for an
// allocation computation to overflow usize, the previous allocation for that
// container must have been filled to a size of usize::MAX/2, which is an
// allocation that would have failed in the allocator. But we check for this to
// be pedantic and to find out if it ever does happen.
//
// No-std abort is implemented using a double panic. On most platforms the
// current mechanism for this is for core::intrinsics::abort to invoke an
// invalid instruction. On Unix, the process will probably terminate with a
// signal like SIGABRT, SIGILL, SIGTRAP, SIGSEGV or SIGBUS. The precise
// behaviour is not guaranteed and not stable, but is safe.
#[cold]
pub(crate) fn die<T>() -> T {
    struct PanicAgain;

    impl Drop for PanicAgain {
        fn drop(&mut self) {
            panic!("arithmetic overflow");
        }
    }

    fn do_die() -> ! {
        let _panic_again = PanicAgain;
        panic!("arithmetic overflow");
    }

    do_die();
}