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
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

pub use arbitrary::{Arbitrary, Error as ArbitraryError, Unstructured};
use canonical::{Canon, Id, Sink};

const FUZZ_ITERATIONS: usize = 128;

fn hash<T: Hash>(t: T) -> u64 {
    let mut hasher = DefaultHasher::new();
    t.hash(&mut hasher);
    hasher.finish()
}

/// Fuzzes a type with regards to its Canon implementation.
/// making sure every serialization produces an Equal result when deserialized
pub fn fuzz_canon<C>()
where
    C: Canon + Arbitrary + PartialEq + std::fmt::Debug,
{
    fuzz_canon_iterations::<C>(FUZZ_ITERATIONS)
}

/// Fuzzes for a set number of iterations
pub fn fuzz_canon_iterations<C>(iterations: usize)
where
    C: Canon + Arbitrary + PartialEq + std::fmt::Debug,
{
    let mut entropy = 0;
    for _ in 0..iterations {
        let mut bytes = vec![];

        let canon = {
            loop {
                match C::arbitrary(&mut Unstructured::new(&bytes)) {
                    Ok(t) => break t,
                    Err(_) => {
                        entropy += 1;

                        bytes.extend_from_slice(&hash(entropy).to_be_bytes());
                    }
                }
            }
        };

        let claimed_len = canon.encoded_len();

        let mut buffer_a = vec![];
        buffer_a.resize_with(claimed_len + 1, || 0xff);

        let mut buffer_b = vec![];
        buffer_b.resize_with(claimed_len + 1, || 0x00);

        let mut sink_a = Sink::new(&mut buffer_a[..]);
        let mut sink_b = Sink::new(&mut buffer_b[..]);

        canon.encode(&mut sink_a);
        canon.encode(&mut sink_b);

        let mut valid = true;

        // assert we did not write past our claimed len

        if buffer_a[claimed_len] != 0xff
            || buffer_b[claimed_len] != 0x00
            || (claimed_len > 0
                && buffer_a[claimed_len - 1] != buffer_b[claimed_len - 1])
        {
            valid = false
        }

        if !valid {
            for i in 0..claimed_len {
                if buffer_a[i] != buffer_b[i] {
                    panic!("claimed {}, wrote {}", claimed_len, i - 1)
                }
            }
        }

        let id = Id::new(&canon);
        let restored = id.reify().unwrap();

        assert!(canon == restored);
    }
}