randomx_rs/
test_utils.rs

1// Copyright 2019. The Tari Project
2//
3// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4// following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7// disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10// following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13// products derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23use crate::{RandomXCache, RandomXDataset, RandomXFlag, RandomXVM};
24
25/// Fuzzing:
26/// - `pub fn randomx_alloc_cache`
27/// - `pub fn randomx_get_flags`
28/// - `pub fn randomx_init_cache`
29/// - `pub fn randomx_release_cache`
30#[allow(clippy::needless_pass_by_value)] // This is required by the `QuickCheck` fuzzing framework
31pub fn fuzz_randomx_alloc_cache(data: Vec<u8>) -> bool {
32    let flags = if data.is_empty() {
33        RandomXFlag::default()
34    } else {
35        match data[0] % 10 {
36            0 => RandomXFlag::get_recommended_flags(),
37            1 => RandomXFlag::FLAG_DEFAULT,
38            2 => RandomXFlag::FLAG_LARGE_PAGES,
39            3 => RandomXFlag::FLAG_HARD_AES,
40            4 => RandomXFlag::FLAG_FULL_MEM,
41            5 => RandomXFlag::FLAG_JIT,
42            6 => RandomXFlag::FLAG_SECURE,
43            7 => RandomXFlag::FLAG_ARGON2_SSSE3,
44            8 => RandomXFlag::FLAG_ARGON2_AVX2,
45            _ => RandomXFlag::FLAG_ARGON2,
46        }
47    };
48    let _unused = RandomXCache::new(flags, &data);
49    true
50}
51
52/// Fuzzing:
53/// - `pub fn randomx_create_vm`
54/// - `pub fn randomx_destroy_vm`
55/// - `pub fn randomx_vm_set_cache`
56/// - `pub fn randomx_alloc_cache`
57/// - `pub fn randomx_get_flags`
58/// - `pub fn randomx_init_cache`
59/// - `pub fn randomx_release_cache`
60#[allow(clippy::needless_pass_by_value)] // This is required by the `QuickCheck` fuzzing framework
61
62pub fn fuzz_randomx_create_vm_with_cache_only(data: Vec<u8>) -> bool {
63    let flags = RandomXFlag::get_recommended_flags();
64    if let Ok(cache) = RandomXCache::new(flags, &data) {
65        if let Ok(mut vm) = RandomXVM::new(flags, Some(cache.clone()), None) {
66            let _unused = vm.reinit_cache(cache);
67        }
68    }
69    true
70}
71
72/// Fuzzing:
73/// - `pub fn randomx_get_flags`
74/// - `pub fn randomx_create_vm`
75/// - `pub fn randomx_destroy_vm`
76/// - `pub fn randomx_dataset_item_count`
77/// - `pub fn randomx_alloc_cache`
78/// - `pub fn randomx_init_cache`
79/// - `pub fn randomx_release_cache`
80/// - `pub fn randomx_alloc_dataset`
81/// - `pub fn randomx_init_dataset`
82/// - `pub fn randomx_release_dataset`
83/// - `pub fn randomx_vm_set_cache`
84/// - `pub fn randomx_vm_set_dataset`
85/// - `pub fn randomx_dataset_item_count`
86/// - `pub fn randomx_get_dataset_memory`
87#[allow(clippy::needless_pass_by_value)] // This is required by the `QuickCheck` fuzzing framework
88pub fn fuzz_randomx_create_vm_with_cache_and_dataset(data: Vec<u8>) -> bool {
89    let flags = RandomXFlag::get_recommended_flags();
90    if let Ok(cache) = RandomXCache::new(flags, &data) {
91        let start = if data.is_empty() { 0u32 } else { u32::from(data[0] % 3) };
92        if let Ok(dataset) = RandomXDataset::new(flags, cache.clone(), start) {
93            for _ in 0..100 {
94                let _unused = dataset.get_data();
95            }
96            if let Ok(mut vm) = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())) {
97                let _unused = vm.reinit_cache(cache);
98                let _unused = vm.reinit_dataset(dataset);
99            }
100        }
101    }
102    true
103}
104
105// Helper function to calculate hashes
106fn calculate_hashes(hash_data: &[u8], vm: &mut RandomXVM, iterations: u8) {
107    let mut hash_data = hash_data.to_vec();
108    for _ in 0..iterations {
109        if hash_data.len() > 1 {
110            // Prepare hash input data
111            let hash_set_len = 12;
112            let mut hash_set = Vec::with_capacity(hash_set_len);
113            for _ in 0..hash_set_len {
114                let mut scratch_data = hash_data.clone();
115                let last = scratch_data.pop().unwrap();
116                scratch_data.insert(0, last);
117                hash_set.push(scratch_data);
118            }
119            let hash_set_ref = hash_set.iter().map(|v| v.as_slice()).collect::<Vec<&[u8]>>();
120            // Fuzz hash
121            let _unused = vm.calculate_hash(&hash_data);
122            let _unused = vm.calculate_hash_set(&hash_set_ref);
123            // Change data set
124            hash_data.pop();
125        } else {
126            let _unused = vm.calculate_hash(&hash_data);
127            let _unused = vm.calculate_hash_set(&[&hash_data]);
128        }
129    }
130}
131
132/// Fuzzing:
133/// - `pub fn randomx_calculate_hash`
134/// - `pub fn randomx_calculate_hash_last`
135/// - `pub fn randomx_calculate_hash_first`
136/// - `pub fn randomx_calculate_hash_next`
137/// Secondary:
138/// - `pub fn randomx_create_vm`
139/// - `pub fn randomx_destroy_vm`
140/// - `pub fn randomx_alloc_cache`
141/// - `pub fn randomx_get_flags`
142/// - `pub fn randomx_init_cache`
143/// - `pub fn randomx_release_cache`
144#[allow(clippy::needless_pass_by_value)] // This is required by the `QuickCheck` fuzzing framework
145pub fn fuzz_randomx_vm_calculate_hash_with_cache_only(data: Vec<u8>) -> bool {
146    let flags = RandomXFlag::get_recommended_flags();
147    if let Ok(cache) = RandomXCache::new(flags, &data) {
148        let vm = RandomXVM::new(flags, Some(cache), None);
149        if let Ok(mut vm) = vm {
150            calculate_hashes(&data, &mut vm, 100);
151        }
152    }
153    true
154}
155
156/// Fuzzing:
157/// - `pub fn randomx_calculate_hash`
158/// - `pub fn randomx_calculate_hash_last`
159/// - `pub fn randomx_calculate_hash_first`
160/// - `pub fn randomx_calculate_hash_next`
161/// Secondary:
162/// - `pub fn randomx_create_vm`
163/// - `pub fn randomx_destroy_vm`
164/// - `pub fn randomx_alloc_cache`
165/// - `pub fn randomx_get_flags`
166/// - `pub fn randomx_init_cache`
167/// - `pub fn randomx_release_cache`
168/// - `pub fn randomx_alloc_dataset`
169/// - `pub fn randomx_init_dataset`
170/// - `pub fn randomx_dataset_item_count`
171/// - `pub fn randomx_get_dataset_memory`
172/// - `pub fn randomx_release_dataset`
173#[allow(clippy::needless_pass_by_value)] // This is required by the `QuickCheck` fuzzing framework
174pub fn fuzz_randomx_vm_calculate_hash_with_cache_and_dataset(data: Vec<u8>) -> bool {
175    let flags = RandomXFlag::get_recommended_flags();
176    if let Ok(cache) = RandomXCache::new(flags, &data) {
177        if let Ok(dataset) = RandomXDataset::new(flags, cache.clone(), 0) {
178            let vm = RandomXVM::new(flags, Some(cache), Some(dataset.clone()));
179            if let Ok(mut vm) = vm {
180                calculate_hashes(&data, &mut vm, 100);
181            }
182        }
183    }
184    true
185}
186
187#[cfg(test)]
188mod tests {
189    use quickcheck::QuickCheck;
190
191    use crate::test_utils::{
192        fuzz_randomx_alloc_cache,
193        fuzz_randomx_create_vm_with_cache_and_dataset,
194        fuzz_randomx_create_vm_with_cache_only,
195        fuzz_randomx_vm_calculate_hash_with_cache_and_dataset,
196        fuzz_randomx_vm_calculate_hash_with_cache_only,
197    };
198
199    #[test]
200    fn test_fuzz_lib_alloc_cache() {
201        fuzz_randomx_alloc_cache(vec![]);
202        const TESTS: u64 = 25;
203        QuickCheck::new()
204            .min_tests_passed(TESTS)
205            .tests(TESTS)
206            .max_tests(TESTS)
207            .quickcheck(fuzz_randomx_alloc_cache as fn(Vec<u8>) -> bool);
208    }
209
210    #[test]
211    fn test_fuzz_randomx_create_vm_with_cache_only() {
212        fuzz_randomx_create_vm_with_cache_only(vec![]);
213        const TESTS: u64 = 25;
214        QuickCheck::new()
215            .min_tests_passed(TESTS)
216            .tests(TESTS)
217            .max_tests(TESTS)
218            .quickcheck(fuzz_randomx_create_vm_with_cache_only as fn(Vec<u8>) -> bool);
219    }
220
221    #[test]
222    fn test_fuzz_randomx_create_vm_with_cache_and_dataset() {
223        fuzz_randomx_create_vm_with_cache_and_dataset(vec![]);
224        const TESTS: u64 = 1;
225        QuickCheck::new()
226            .min_tests_passed(TESTS)
227            .tests(TESTS)
228            .max_tests(TESTS)
229            .quickcheck(fuzz_randomx_create_vm_with_cache_and_dataset as fn(Vec<u8>) -> bool);
230    }
231
232    #[test]
233    fn test_fuzz_randomx_vm_calculate_hash_with_cache_only() {
234        fuzz_randomx_vm_calculate_hash_with_cache_only(vec![]);
235        const TESTS: u64 = 3;
236        QuickCheck::new()
237            .min_tests_passed(TESTS)
238            .tests(TESTS)
239            .max_tests(TESTS)
240            .quickcheck(fuzz_randomx_vm_calculate_hash_with_cache_only as fn(Vec<u8>) -> bool);
241    }
242
243    #[test]
244    fn test_fuzz_randomx_vm_calculate_hash_with_cache_and_dataset() {
245        fuzz_randomx_vm_calculate_hash_with_cache_and_dataset(vec![]);
246        const TESTS: u64 = 1;
247        QuickCheck::new()
248            .min_tests_passed(TESTS)
249            .tests(TESTS)
250            .max_tests(TESTS)
251            .quickcheck(fuzz_randomx_vm_calculate_hash_with_cache_and_dataset as fn(Vec<u8>) -> bool);
252    }
253}