blob: 5950ac24d190702996af14e16f75c074d62f6c24 [file] [log] [blame]
// Copyright 2021, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(missing_docs)]
#![allow(unused_must_use)]
#![no_main]
use libfuzzer_sys::arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
use rustutils::system_properties;
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{fmt, thread, time};
thread_local! {
static COUNTER: RefCell<u64> = RefCell::new(0);
}
#[derive(Arbitrary, Clone, Debug)]
enum WritableProperty {
Fuzzer1,
Fuzzer2,
}
#[derive(Arbitrary, Clone, Debug)]
enum Property {
KeystoreBootLevel,
Random { name: String },
Unique,
Writable { prop: WritableProperty },
}
impl fmt::Display for Property {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", match self {
Property::KeystoreBootLevel => "keystore.boot_level".to_string(),
Property::Random { name } => name.to_string(),
Property::Unique => COUNTER.with(|counter| {
let val = *counter.borrow();
*counter.borrow_mut() += 1;
format!("unique.fuzz.prop.{}", val)
}),
Property::Writable { prop } => prop.to_string(),
})
}
}
impl fmt::Display for WritableProperty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", match self {
WritableProperty::Fuzzer1 => "unique.fuzz.prop".to_string(),
WritableProperty::Fuzzer2 => "unique.fuzz.two.prop".to_string(),
})
}
}
#[derive(Arbitrary, Debug)]
enum Command {
Read { prop: Property },
Write { prop: WritableProperty, value: String },
WatcherRead { prop: Property },
WatcherWait { value: u8 },
}
fuzz_target!(|commands: Vec<Command>| {
for command in commands {
match command {
Command::Read { prop } => {
system_properties::read(&prop.to_string());
}
Command::Write { prop, value } => {
system_properties::write(&prop.to_string(), &value);
}
Command::WatcherRead { prop } => {
if let Ok(mut watcher) = system_properties::PropertyWatcher::new(&prop.to_string()) {
watcher.read(|_n, v| Ok(v.to_string()));
}
}
Command::WatcherWait { value } => {
// We want to ensure that we choose a property that can be written,
// or else we'd just have to implement a timeout and do nothing,
// so we use a hardcoded valid property.
let prop_str = "keystore.boot_level";
let waited = Arc::new(AtomicBool::new(false));
let waited_clone = waited.clone();
// Spawn a thread that will wait for a change to the property.
let waiter = thread::spawn(move || {
let result = match system_properties::PropertyWatcher::new(prop_str) {
Ok(mut watcher) => watcher.wait(),
Err(e) => Err(e),
};
waited_clone.store(true, Ordering::Relaxed);
result
});
// Write the property in a loop (so we're sure to follow the wait call).
let mut cur_value = value;
while !waited.load(Ordering::Relaxed) {
thread::sleep(time::Duration::from_millis(1));
system_properties::write(prop_str, &cur_value.to_string());
cur_value = cur_value.wrapping_add(1);
}
waiter.join();
}
}
}
});