From c9318108305c79ab3b3f510e12249d82ce5d54d1 Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Sat, 18 Jan 2020 07:54:36 +0000 Subject: [PATCH] Bug 1608624 - Add more variation to RKV function calls fuzzing by interspersing fuzzing data with fuzzing opts, r=truber Differential Revision: https://phabricator.services.mozilla.com/D59619 --HG-- extra : moz-landing-system : lando --- Cargo.lock | 1 + tools/fuzzing/rust/Cargo.toml | 4 + tools/fuzzing/rust/src/lib.rs | 166 +++++++++++++++++++++++----------- 3 files changed, 117 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53bbd2b5168a..13dea5a27e86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,6 +1249,7 @@ version = "0.1.0" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "lmdb-rkv 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "rkv 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/tools/fuzzing/rust/Cargo.toml b/tools/fuzzing/rust/Cargo.toml index 3d82e9b76efd..ba8daa4b62e0 100644 --- a/tools/fuzzing/rust/Cargo.toml +++ b/tools/fuzzing/rust/Cargo.toml @@ -11,3 +11,7 @@ lazy_static = "1.4.0" [dependencies.rkv] version = "0.10.2" features = ["with-fuzzer-no-link"] + +[dependencies.lmdb-rkv] +version = "0.12.3" +features = ["with-fuzzer-no-link"] \ No newline at end of file diff --git a/tools/fuzzing/rust/src/lib.rs b/tools/fuzzing/rust/src/lib.rs index f3f0405014cc..04d2c693050b 100644 --- a/tools/fuzzing/rust/src/lib.rs +++ b/tools/fuzzing/rust/src/lib.rs @@ -5,6 +5,7 @@ #[macro_use] extern crate lazy_static; extern crate libc; +extern crate lmdb; extern crate rkv; extern crate tempfile; @@ -27,6 +28,11 @@ fn eat_lmdb_err(value: Result) -> Result, rkv:: } } +fn panic_with_err(err: rkv::StoreError) { + println!("Got error: {}", err); + Result::Err(err).unwrap() +} + #[no_mangle] pub extern "C" fn fuzz_rkv_db_file(raw_data: *const u8, size: libc::size_t) -> libc::c_int { let data = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize) }; @@ -125,38 +131,126 @@ lazy_static! { #[no_mangle] pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> libc::c_int { - let mut fuzz = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize).to_vec() }; + let data = unsafe { std::slice::from_raw_parts(raw_data as *const u8, size as usize) }; + let mut fuzz = data.iter().copied(); - fn maybe_do(fuzz: &mut Vec, f: impl FnOnce(&mut Vec) -> ()) -> Option<()> { - match fuzz.pop().map(|byte| byte % 2) { + fn maybe_do>(fuzz: &mut I, f: impl FnOnce(&mut I) -> ()) -> Option<()> { + match fuzz.next().map(|byte| byte % 2) { Some(0) => Some(f(fuzz)), _ => None } } - fn maybe_abort(fuzz: &mut Vec, read: rkv::Reader) -> Result<(), rkv::StoreError> { - match fuzz.pop().map(|byte| byte % 2) { + fn maybe_abort>(fuzz: &mut I, read: rkv::Reader) -> Result<(), rkv::StoreError> { + match fuzz.next().map(|byte| byte % 2) { Some(0) => Ok(read.abort()), _ => Ok(()) } }; - fn maybe_commit(fuzz: &mut Vec, write: rkv::Writer) -> Result<(), rkv::StoreError> { - match fuzz.pop().map(|byte| byte % 3) { + fn maybe_commit>(fuzz: &mut I, write: rkv::Writer) -> Result<(), rkv::StoreError> { + match fuzz.next().map(|byte| byte % 3) { Some(0) => write.commit(), Some(1) => Ok(write.abort()), _ => Ok(()) } }; - fn get_static_data<'a>(fuzz: &mut Vec) -> Option<&'a String> { - fuzz.pop().map(|byte| { + fn get_static_data<'a, I: Iterator>(fuzz: &mut I) -> Option<&'a String> { + fuzz.next().map(|byte| { let data = &*STATIC_DATA; let n = byte as usize; data.get(n % data.len()).unwrap() }) }; + fn get_fuzz_data + Clone>(fuzz: &mut I, max_len: usize) -> Option> { + fuzz.next().map(|byte| { + let n = byte as usize; + fuzz.clone().take((n * n) % max_len).collect() + }) + }; + + fn get_any_data + Clone>(fuzz: &mut I, max_len: usize) -> Option> { + match fuzz.next().map(|byte| byte % 2) { + Some(0) => get_static_data(fuzz).map(|v| v.clone().into_bytes()), + Some(1) => get_fuzz_data(fuzz, max_len), + _ => None + } + }; + + fn store_put + Clone>(fuzz: &mut I, env: &rkv::Rkv, store: &rkv::SingleStore) { + let key = match get_any_data(fuzz, 1024) { + Some(key) => key, + None => return + }; + let value = match get_any_data(fuzz, std::usize::MAX) { + Some(value) => value, + None => return + }; + + let mut writer = env.write().unwrap(); + let mut full = false; + + match store.put(&mut writer, key, &rkv::Value::Blob(&value)) { + Ok(_) => {}, + Err(rkv::StoreError::LmdbError(lmdb::Error::BadValSize)) => {}, + Err(rkv::StoreError::LmdbError(lmdb::Error::MapFull)) => full = true, + Err(err) => panic_with_err(err) + }; + + if full { + writer.abort(); + store_resize(fuzz, env); + } else { + maybe_commit(fuzz, writer).unwrap(); + } + } + + fn store_get + Clone>(fuzz: &mut I, env: &rkv::Rkv, store: &rkv::SingleStore) { + let key = match get_any_data(fuzz, 1024) { + Some(key) => key, + None => return + }; + + let mut reader = match env.read() { + Ok(reader) => reader, + Err(rkv::StoreError::LmdbError(lmdb::Error::ReadersFull)) => return, + Err(err) => return panic_with_err(err) + }; + + match store.get(&mut reader, key) { + Ok(_) => {}, + Err(rkv::StoreError::LmdbError(lmdb::Error::BadValSize)) => {}, + Err(err) => panic_with_err(err) + }; + + maybe_abort(fuzz, reader).unwrap(); + } + + fn store_delete + Clone>(fuzz: &mut I, env: &rkv::Rkv, store: &rkv::SingleStore) { + let key = match get_any_data(fuzz, 1024) { + Some(key) => key, + None => return + }; + + let mut writer = env.write().unwrap(); + + match store.delete(&mut writer, key) { + Ok(_) => {}, + Err(rkv::StoreError::LmdbError(lmdb::Error::BadValSize)) => {}, + Err(rkv::StoreError::LmdbError(lmdb::Error::NotFound)) => {}, + Err(err) => panic_with_err(err) + }; + + maybe_commit(fuzz, writer).unwrap(); + } + + fn store_resize>(fuzz: &mut I, env: &rkv::Rkv) { + let n = fuzz.next().unwrap_or(1) as usize; + env.set_map_size(1_048_576 * (n % 100)).unwrap() // 1,048,576 bytes, i.e. 1MiB. + }; + let root = Builder::new().prefix("fuzz_rkv_calls").tempdir().unwrap(); fs::create_dir_all(root.path()).unwrap(); @@ -164,15 +258,15 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib builder.set_max_dbs(1); // need at least one db maybe_do(&mut fuzz, |fuzz| { - let n = fuzz.pop().unwrap_or(126) as u32; // default + let n = fuzz.next().unwrap_or(126) as u32; // default builder.set_max_readers(1 + n); }); maybe_do(&mut fuzz, |fuzz| { - let n = fuzz.pop().unwrap_or(0) as u32; + let n = fuzz.next().unwrap_or(0) as u32; builder.set_max_dbs(1 + n); }); maybe_do(&mut fuzz, |fuzz| { - let n = fuzz.pop().unwrap_or(1) as usize; + let n = fuzz.next().unwrap_or(1) as usize; builder.set_map_size(1_048_576 * (n % 100)); // 1,048,576 bytes, i.e. 1MiB. }); @@ -180,48 +274,12 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib let store = env.open_single("test", rkv::StoreOptions::create()).unwrap(); loop { - match fuzz.pop().map(|byte| byte % 4) { - Some(0) => { - let key = match get_static_data(&mut fuzz) { - Some(value) => value, - None => break - }; - let value = match get_static_data(&mut fuzz) { - Some(value) => value, - None => break - }; - let mut writer = env.write().unwrap(); - eat_lmdb_err(store.put(&mut writer, key, &rkv::Value::Str(value))).unwrap(); - maybe_commit(&mut fuzz, writer).unwrap(); - } - Some(1) => { - let key = match get_static_data(&mut fuzz) { - Some(value) => value, - None => break - }; - let mut reader = env.read().unwrap(); - eat_lmdb_err(store.get(&mut reader, key)).unwrap(); - maybe_abort(&mut fuzz, reader).unwrap(); - } - Some(2) => { - let key = match get_static_data(&mut fuzz) { - Some(value) => value, - None => break - }; - let mut writer = env.write().unwrap(); - eat_lmdb_err(store.delete(&mut writer, key)).unwrap(); - maybe_commit(&mut fuzz, writer).unwrap(); - } - Some(3) => { - // Any attempt to set a size smaller than the space already - // consumed by the environment will be silently changed to the - // current size of the used space. - let n = fuzz.pop().unwrap_or(1) as usize; - builder.set_map_size(1_048_576 * (n % 100)); // 1,048,576 bytes, i.e. 1MiB. - } - _ => { - break - } + match fuzz.next().map(|byte| byte % 4) { + Some(0) => store_put(&mut fuzz, &env, &store), + Some(1) => store_get(&mut fuzz, &env, &store), + Some(2) => store_delete(&mut fuzz, &env, &store), + Some(3) => store_resize(&mut fuzz, &env), + _ => break } }