unsafe_libyaml/
ops.rs

1pub(crate) trait ForceAdd: Sized {
2    fn force_add(self, rhs: Self) -> Self;
3}
4
5impl ForceAdd for u8 {
6    fn force_add(self, rhs: Self) -> Self {
7        self.checked_add(rhs).unwrap_or_else(die)
8    }
9}
10
11impl ForceAdd for i32 {
12    fn force_add(self, rhs: Self) -> Self {
13        self.checked_add(rhs).unwrap_or_else(die)
14    }
15}
16
17impl ForceAdd for u32 {
18    fn force_add(self, rhs: Self) -> Self {
19        self.checked_add(rhs).unwrap_or_else(die)
20    }
21}
22
23impl ForceAdd for u64 {
24    fn force_add(self, rhs: Self) -> Self {
25        self.checked_add(rhs).unwrap_or_else(die)
26    }
27}
28
29impl ForceAdd for usize {
30    fn force_add(self, rhs: Self) -> Self {
31        self.checked_add(rhs).unwrap_or_else(die)
32    }
33}
34
35pub(crate) trait ForceMul: Sized {
36    fn force_mul(self, rhs: Self) -> Self;
37}
38
39impl ForceMul for i32 {
40    fn force_mul(self, rhs: Self) -> Self {
41        self.checked_mul(rhs).unwrap_or_else(die)
42    }
43}
44
45impl ForceMul for i64 {
46    fn force_mul(self, rhs: Self) -> Self {
47        self.checked_mul(rhs).unwrap_or_else(die)
48    }
49}
50
51impl ForceMul for u64 {
52    fn force_mul(self, rhs: Self) -> Self {
53        self.checked_mul(rhs).unwrap_or_else(die)
54    }
55}
56
57pub(crate) trait ForceInto {
58    fn force_into<U>(self) -> U
59    where
60        Self: TryInto<U>;
61}
62
63impl<T> ForceInto for T {
64    fn force_into<U>(self) -> U
65    where
66        Self: TryInto<U>,
67    {
68        <Self as TryInto<U>>::try_into(self)
69            .ok()
70            .unwrap_or_else(die)
71    }
72}
73
74// Deterministically abort on arithmetic overflow, instead of wrapping and
75// continuing with invalid behavior.
76//
77// This is impossible or nearly impossible to hit as the arithmetic computations
78// in libyaml are all related to either:
79//
80//  - small integer processing (ascii, hex digits)
81//  - allocation sizing
82//
83// and the only allocations in libyaml are for fixed-sized objects and
84// geometrically growing buffers with a growth factor of 2. So in order for an
85// allocation computation to overflow usize, the previous allocation for that
86// container must have been filled to a size of usize::MAX/2, which is an
87// allocation that would have failed in the allocator. But we check for this to
88// be pedantic and to find out if it ever does happen.
89//
90// No-std abort is implemented using a double panic. On most platforms the
91// current mechanism for this is for core::intrinsics::abort to invoke an
92// invalid instruction. On Unix, the process will probably terminate with a
93// signal like SIGABRT, SIGILL, SIGTRAP, SIGSEGV or SIGBUS. The precise
94// behaviour is not guaranteed and not stable, but is safe.
95#[cold]
96pub(crate) fn die<T>() -> T {
97    struct PanicAgain;
98
99    impl Drop for PanicAgain {
100        fn drop(&mut self) {
101            panic!("arithmetic overflow");
102        }
103    }
104
105    fn do_die() -> ! {
106        let _panic_again = PanicAgain;
107        panic!("arithmetic overflow");
108    }
109
110    do_die();
111}