Bug 1932170 - Add missing tools namespace on a CLOSED TREE.
[gecko.git] / tools / fuzzing / rust / src / lib.rs
blob25c9195fb809f32327be01cb1e46ca1296091eb1
1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 #[macro_use]
6 extern crate lazy_static;
7 extern crate libc;
8 extern crate lmdb;
9 extern crate rkv;
10 extern crate tempfile;
12 use rkv::backend::{
13     BackendEnvironmentBuilder, SafeMode, SafeModeDatabase, SafeModeEnvironment,
14     SafeModeRoTransaction, SafeModeRwTransaction,
16 use std::fs;
17 use std::fs::File;
18 use std::io::Write;
19 use std::iter;
20 use std::path::Path;
21 use std::sync::Arc;
22 use std::thread;
23 use tempfile::Builder;
25 type Rkv = rkv::Rkv<SafeModeEnvironment>;
26 type SingleStore = rkv::SingleStore<SafeModeDatabase>;
28 fn eat_lmdb_err<T>(value: Result<T, rkv::StoreError>) -> Result<Option<T>, rkv::StoreError> {
29     match value {
30         Ok(value) => Ok(Some(value)),
31         Err(rkv::StoreError::LmdbError(_)) => Ok(None),
32         Err(err) => {
33             println!("Not a crash, but an error outside LMDB: {}", err);
34             println!("A refined fuzzing test, or changes to RKV, might be required.");
35             Err(err)
36         }
37     }
40 fn panic_with_err(err: rkv::StoreError) {
41     println!("Got error: {}", err);
42     Result::Err(err).unwrap()
45 #[no_mangle]
46 pub extern "C" fn fuzz_rkv_db_file(raw_data: *const u8, size: libc::size_t) -> libc::c_int {
47     let data = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize) };
49     // First 8192 bytes are for the lock file.
50     if data.len() < 8192 {
51         return 0;
52     }
53     let (lock, db) = data.split_at(8192);
54     let mut lock_file = File::create("data.lock").unwrap();
55     lock_file.write_all(lock).unwrap();
56     let mut db_file = File::create("data.mdb").unwrap();
57     db_file.write_all(db).unwrap();
59     let env = Rkv::with_capacity::<SafeMode>(Path::new("."), 2).unwrap();
60     let store = env
61         .open_single("test", rkv::StoreOptions::create())
62         .unwrap();
64     let reader = env.read().unwrap();
65     eat_lmdb_err(store.get(&reader, &[0])).unwrap();
67     0
70 #[no_mangle]
71 pub extern "C" fn fuzz_rkv_db_name(raw_data: *const u8, size: libc::size_t) -> libc::c_int {
72     let data = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize) };
74     let root = Builder::new().prefix("fuzz_rkv_db_name").tempdir().unwrap();
75     fs::create_dir_all(root.path()).unwrap();
77     let env = Rkv::new::<SafeMode>(root.path()).unwrap();
78     let name = String::from_utf8_lossy(data);
79     println!("Checking string: '{:?}'", name);
80     // Some strings are invalid database names, and are handled as store errors.
81     // Ignore those errors, but not others.
82     let store = eat_lmdb_err(env.open_single(name.as_ref(), rkv::StoreOptions::create())).unwrap();
84     if let Some(store) = store {
85         let reader = env.read().unwrap();
86         eat_lmdb_err(store.get(&reader, &[0])).unwrap();
87     };
89     0
92 #[no_mangle]
93 pub extern "C" fn fuzz_rkv_key_write(raw_data: *const u8, size: libc::size_t) -> libc::c_int {
94     let data = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize) };
96     let root = Builder::new()
97         .prefix("fuzz_rkv_key_write")
98         .tempdir()
99         .unwrap();
100     fs::create_dir_all(root.path()).unwrap();
102     let env = Rkv::new::<SafeMode>(root.path()).unwrap();
103     let store = env
104         .open_single("test", rkv::StoreOptions::create())
105         .unwrap();
107     let mut writer = env.write().unwrap();
108     // Some data are invalid values, and are handled as store errors.
109     // Ignore those errors, but not others.
110     eat_lmdb_err(store.put(&mut writer, data, &rkv::Value::Str("val"))).unwrap();
112     0
115 #[no_mangle]
116 pub extern "C" fn fuzz_rkv_val_write(raw_data: *const u8, size: libc::size_t) -> libc::c_int {
117     let data = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize) };
119     let root = Builder::new()
120         .prefix("fuzz_rkv_val_write")
121         .tempdir()
122         .unwrap();
123     fs::create_dir_all(root.path()).unwrap();
125     let env = Rkv::new::<SafeMode>(root.path()).unwrap();
126     let store = env
127         .open_single("test", rkv::StoreOptions::create())
128         .unwrap();
130     let mut writer = env.write().unwrap();
131     let string = String::from_utf8_lossy(data);
132     let value = rkv::Value::Str(&string);
133     store.put(&mut writer, "key", &value).unwrap();
135     0
138 lazy_static! {
139     static ref STATIC_DATA: Vec<String> = {
140         let sizes = vec![4, 16, 128, 512, 1024];
141         let mut data = Vec::new();
143         for (i, s) in sizes.into_iter().enumerate() {
144             let bytes = iter::repeat('a' as u8 + i as u8).take(s).collect();
145             data.push(String::from_utf8(bytes).unwrap());
146         }
148         data
149     };
152 #[no_mangle]
153 pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> libc::c_int {
154     let data = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize) };
155     let mut fuzz = data.iter().copied();
157     fn maybe_do<I: Iterator<Item = u8>>(fuzz: &mut I, f: impl FnOnce(&mut I) -> ()) -> Option<()> {
158         match fuzz.next().map(|byte| byte % 2) {
159             Some(0) => Some(f(fuzz)),
160             _ => None,
161         }
162     }
164     fn maybe_abort<I: Iterator<Item = u8>>(
165         fuzz: &mut I,
166         read: rkv::Reader<SafeModeRoTransaction>,
167     ) -> Result<(), rkv::StoreError> {
168         match fuzz.next().map(|byte| byte % 2) {
169             Some(0) => Ok(read.abort()),
170             _ => Ok(()),
171         }
172     }
174     fn maybe_commit<I: Iterator<Item = u8>>(
175         fuzz: &mut I,
176         write: rkv::Writer<SafeModeRwTransaction>,
177     ) -> Result<(), rkv::StoreError> {
178         match fuzz.next().map(|byte| byte % 3) {
179             Some(0) => write.commit(),
180             Some(1) => Ok(write.abort()),
181             _ => Ok(()),
182         }
183     }
185     fn get_static_data<'a, I: Iterator<Item = u8>>(fuzz: &mut I) -> Option<&'a String> {
186         fuzz.next().map(|byte| {
187             let data = &*STATIC_DATA;
188             let n = byte as usize;
189             data.get(n % data.len()).unwrap()
190         })
191     }
193     fn get_fuzz_data<I: Iterator<Item = u8> + Clone>(
194         fuzz: &mut I,
195         max_len: usize,
196     ) -> Option<Vec<u8>> {
197         fuzz.next().map(|byte| {
198             let n = byte as usize;
199             fuzz.clone().take((n * n) % max_len).collect()
200         })
201     }
203     fn get_any_data<I: Iterator<Item = u8> + Clone>(
204         fuzz: &mut I,
205         max_len: usize,
206     ) -> Option<Vec<u8>> {
207         match fuzz.next().map(|byte| byte % 2) {
208             Some(0) => get_static_data(fuzz).map(|v| v.clone().into_bytes()),
209             Some(1) => get_fuzz_data(fuzz, max_len),
210             _ => None,
211         }
212     }
214     fn store_put<I: Iterator<Item = u8> + Clone>(fuzz: &mut I, env: &Rkv, store: &SingleStore) {
215         let key = match get_any_data(fuzz, 1024) {
216             Some(key) => key,
217             None => return,
218         };
219         let value = match get_any_data(fuzz, std::usize::MAX) {
220             Some(value) => value,
221             None => return,
222         };
224         let mut writer = env.write().unwrap();
225         let mut full = false;
227         match store.put(&mut writer, key, &rkv::Value::Blob(&value)) {
228             Ok(_) => {}
229             Err(rkv::StoreError::LmdbError(lmdb::Error::BadValSize)) => {}
230             Err(rkv::StoreError::LmdbError(lmdb::Error::MapFull)) => full = true,
231             Err(err) => panic_with_err(err),
232         };
234         if full {
235             writer.abort();
236             store_resize(fuzz, env);
237         } else {
238             maybe_commit(fuzz, writer).unwrap();
239         }
240     }
242     fn store_get<I: Iterator<Item = u8> + Clone>(fuzz: &mut I, env: &Rkv, store: &SingleStore) {
243         let key = match get_any_data(fuzz, 1024) {
244             Some(key) => key,
245             None => return,
246         };
248         let mut reader = match env.read() {
249             Ok(reader) => reader,
250             Err(rkv::StoreError::LmdbError(lmdb::Error::ReadersFull)) => return,
251             Err(err) => return panic_with_err(err),
252         };
254         match store.get(&mut reader, key) {
255             Ok(_) => {}
256             Err(rkv::StoreError::LmdbError(lmdb::Error::BadValSize)) => {}
257             Err(err) => panic_with_err(err),
258         };
260         maybe_abort(fuzz, reader).unwrap();
261     }
263     fn store_delete<I: Iterator<Item = u8> + Clone>(fuzz: &mut I, env: &Rkv, store: &SingleStore) {
264         let key = match get_any_data(fuzz, 1024) {
265             Some(key) => key,
266             None => return,
267         };
269         let mut writer = env.write().unwrap();
271         match store.delete(&mut writer, key) {
272             Ok(_) => {}
273             Err(rkv::StoreError::LmdbError(lmdb::Error::BadValSize)) => {}
274             Err(rkv::StoreError::LmdbError(lmdb::Error::NotFound)) => {}
275             Err(err) => panic_with_err(err),
276         };
278         maybe_commit(fuzz, writer).unwrap();
279     }
281     fn store_resize<I: Iterator<Item = u8>>(fuzz: &mut I, env: &Rkv) {
282         let n = fuzz.next().unwrap_or(1) as usize;
283         env.set_map_size(1_048_576 * (n % 100)).unwrap() // 1,048,576 bytes, i.e. 1MiB.
284     }
286     let root = Builder::new().prefix("fuzz_rkv_calls").tempdir().unwrap();
287     fs::create_dir_all(root.path()).unwrap();
289     let mut builder: SafeMode = Rkv::environment_builder();
290     builder.set_max_dbs(1); // need at least one db
292     maybe_do(&mut fuzz, |fuzz| {
293         let n = fuzz.next().unwrap_or(126) as u32; // default
294         builder.set_max_readers(1 + n);
295     });
296     maybe_do(&mut fuzz, |fuzz| {
297         let n = fuzz.next().unwrap_or(0) as u32;
298         builder.set_max_dbs(1 + n);
299     });
300     maybe_do(&mut fuzz, |fuzz| {
301         let n = fuzz.next().unwrap_or(1) as usize;
302         builder.set_map_size(1_048_576 * (n % 100)); // 1,048,576 bytes, i.e. 1MiB.
303     });
305     let env = Rkv::from_builder(root.path(), builder).unwrap();
306     let store = env
307         .open_single("test", rkv::StoreOptions::create())
308         .unwrap();
310     let shared_env = Arc::new(env);
311     let shared_store = Arc::new(store);
313     let use_threads = fuzz.next().map(|byte| byte % 10 == 0).unwrap_or(false);
314     let max_threads = if use_threads { 16 } else { 1 };
315     let num_threads = fuzz.next().unwrap_or(0) as usize % max_threads + 1;
316     let chunk_size = fuzz.len() / num_threads;
318     let threads = (0..num_threads).map(|_| {
319         let env = shared_env.clone();
320         let store = shared_store.clone();
322         let chunk: Vec<_> = fuzz.by_ref().take(chunk_size).collect();
323         let mut fuzz = chunk.into_iter();
325         thread::spawn(move || loop {
326             match fuzz.next().map(|byte| byte % 4) {
327                 Some(0) => store_put(&mut fuzz, &env, &store),
328                 Some(1) => store_get(&mut fuzz, &env, &store),
329                 Some(2) => store_delete(&mut fuzz, &env, &store),
330                 Some(3) => store_resize(&mut fuzz, &env),
331                 _ => break,
332             }
333         })
334     });
336     for handle in threads {
337         handle.join().unwrap()
338     }
340     0