Fuzzer for rust parcels
Fuzz rust backend of the parcel from libbinder_rs.
Bug: 164122727
Test: m parcel_fuzzer_rs && adb shell data/fuzz/x86_64/parcel_fuzzer_rs/parcel_fuzzer_rs
Change-Id: I113fb85358e91f4235ca524f8e063bf184eff556
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
new file mode 100644
index 0000000..28e0200
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -0,0 +1,25 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+rust_fuzz {
+ name: "parcel_fuzzer_rs",
+ srcs: [
+ "parcel_fuzzer.rs",
+ ],
+ rustlibs: [
+ "libarbitrary",
+ "libnum_traits",
+ "libbinder_rs",
+ "libbinder_random_parcel_rs",
+ "binderReadParcelIface-rust",
+ ],
+
+ fuzz_config: {
+ cc: [
+ "waghpawan@google.com",
+ "smoreland@google.com",
+ ],
+ },
+}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
new file mode 100644
index 0000000..21271d2
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 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)]
+#![no_main]
+
+#[macro_use]
+extern crate libfuzzer_sys;
+
+mod read_utils;
+
+use crate::read_utils::get_read_funcs;
+use binder::binder_impl::{
+ Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode,
+};
+use binder::{
+ declare_binder_interface, BinderFeatures, Interface, Parcelable, ParcelableHolder, SpIBinder,
+ StatusCode,
+};
+use binder_random_parcel_rs::create_random_parcel;
+use libfuzzer_sys::arbitrary::Arbitrary;
+
+#[derive(Arbitrary, Debug)]
+enum ReadOperations {
+ SetDataPosition { pos: i32 },
+ GetDataSize,
+ ReadParcelableHolder { is_vintf: bool },
+ ReadBasicTypes { indexes: Vec<usize> },
+}
+
+#[derive(Arbitrary, Debug)]
+enum Operations<'a> {
+ Transact { code: u32, flag: u32, data: &'a [u8] },
+ Append { start: i32, len: i32, data1: &'a [u8], data2: &'a [u8], append_all: bool },
+ Read { indexes: Vec<ReadOperations>, data: &'a [u8] },
+}
+
+/// Interface to fuzz transact with random parcel
+pub trait BinderTransactTest: Interface {}
+
+declare_binder_interface! {
+ BinderTransactTest["Binder_Transact_Test"] {
+ native: BnBinderTransactTest(on_transact),
+ proxy: BpBinderTransactTest,
+ }
+}
+
+impl BinderTransactTest for Binder<BnBinderTransactTest> {}
+
+impl BinderTransactTest for BpBinderTransactTest {}
+
+impl BinderTransactTest for () {}
+
+fn on_transact(
+ _service: &dyn BinderTransactTest,
+ _code: TransactionCode,
+ _parcel: &BorrowedParcel<'_>,
+ _reply: &mut BorrowedParcel<'_>,
+) -> Result<(), StatusCode> {
+ Err(StatusCode::UNKNOWN_ERROR)
+}
+
+fn do_transact(code: u32, data: &[u8], flag: u32) {
+ let p: Parcel = create_random_parcel(data);
+ let spibinder: Option<SpIBinder> =
+ Some(BnBinderTransactTest::new_binder((), BinderFeatures::default()).as_binder());
+ let _reply = spibinder.submit_transact(code, p, flag);
+}
+
+fn do_append_fuzz(start: i32, len: i32, data1: &[u8], data2: &[u8], append_all: bool) {
+ let mut p1 = create_random_parcel(data1);
+ let p2 = create_random_parcel(data2);
+
+ // Fuzz both append methods
+ if append_all {
+ match p1.append_all_from(&p2) {
+ Ok(result) => result,
+ Err(e) => {
+ println!("Error occurred while appending a parcel using append_all_from: {:?}", e)
+ }
+ }
+ } else {
+ match p1.append_from(&p2, start, len) {
+ Ok(result) => result,
+ Err(e) => {
+ println!("Error occurred while appending a parcel using append_from: {:?}", e)
+ }
+ }
+ };
+}
+
+fn do_read_fuzz(read_operations: Vec<ReadOperations>, data: &[u8]) {
+ let read_funcs = get_read_funcs();
+ let parcel = create_random_parcel(data);
+
+ for operation in read_operations {
+ match operation {
+ ReadOperations::SetDataPosition { pos } => {
+ unsafe {
+ // Safety: Safe if pos is less than current size of the parcel.
+ // It relies on C++ code for bound checks
+ match parcel.set_data_position(pos) {
+ Ok(result) => result,
+ Err(e) => println!("error occurred while setting data position: {:?}", e),
+ }
+ }
+ }
+
+ ReadOperations::GetDataSize => {
+ let data_size = parcel.get_data_size();
+ println!("data size from parcel: {:?}", data_size);
+ }
+
+ ReadOperations::ReadParcelableHolder { is_vintf } => {
+ let stability = if is_vintf { Stability::Vintf } else { Stability::Local };
+ let mut holder: ParcelableHolder = ParcelableHolder::new(stability);
+ match holder.read_from_parcel(parcel.borrowed_ref()) {
+ Ok(result) => result,
+ Err(err) => {
+ println!("error occurred while reading from parcel: {:?}", err)
+ }
+ }
+ }
+
+ ReadOperations::ReadBasicTypes { indexes } => {
+ for index in indexes.iter() {
+ let read_index = index % read_funcs.len();
+ read_funcs[read_index](parcel.borrowed_ref());
+ }
+ }
+ }
+ }
+}
+
+fuzz_target!(|operations: Vec<Operations>| {
+ for operation in operations {
+ match operation {
+ Operations::Transact { code, flag, data } => {
+ do_transact(code, data, flag);
+ }
+
+ Operations::Append { start, len, data1, data2, append_all } => {
+ do_append_fuzz(start, len, data1, data2, append_all);
+ }
+
+ Operations::Read { indexes, data } => {
+ do_read_fuzz(indexes, data);
+ }
+ }
+ }
+});
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
new file mode 100644
index 0000000..9b06013
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+use binder::binder_impl::BorrowedParcel;
+use binder::{ParcelFileDescriptor, Parcelable, SpIBinder};
+use binderReadParcelIface::aidl::EmptyParcelable::EmptyParcelable;
+use binderReadParcelIface::aidl::GenericDataParcelable::GenericDataParcelable;
+use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable;
+
+macro_rules! read_parcel_interface {
+ ($data_type:ty) => {
+ |parcel| {
+ let _res = parcel.read::<$data_type>();
+ }
+ };
+}
+
+#[derive(Debug, Default)]
+pub struct SomeParcelable {
+ pub data: i32,
+}
+
+impl binder::Parcelable for SomeParcelable {
+ fn write_to_parcel(
+ &self,
+ parcel: &mut binder::binder_impl::BorrowedParcel,
+ ) -> std::result::Result<(), binder::StatusCode> {
+ parcel.sized_write(|subparcel| subparcel.write(&self.data))
+ }
+
+ fn read_from_parcel(
+ &mut self,
+ parcel: &binder::binder_impl::BorrowedParcel,
+ ) -> std::result::Result<(), binder::StatusCode> {
+ parcel.sized_read(|subparcel| match subparcel.read() {
+ Ok(result) => {
+ self.data = result;
+ Ok(())
+ }
+ Err(e) => Err(e),
+ })
+ }
+}
+
+binder::impl_deserialize_for_parcelable!(SomeParcelable);
+
+pub fn get_read_funcs() -> Vec<Box<dyn Fn(&BorrowedParcel<'_>)>> {
+ let read_funcs: Vec<Box<dyn Fn(&BorrowedParcel<'_>)>> = vec![
+ //read basic types
+ Box::new(read_parcel_interface!(bool)),
+ Box::new(read_parcel_interface!(i8)),
+ Box::new(read_parcel_interface!(i32)),
+ Box::new(read_parcel_interface!(i64)),
+ Box::new(read_parcel_interface!(f32)),
+ Box::new(read_parcel_interface!(f64)),
+ Box::new(read_parcel_interface!(u16)),
+ Box::new(read_parcel_interface!(u32)),
+ Box::new(read_parcel_interface!(u64)),
+ Box::new(read_parcel_interface!(String)),
+ //read vec of basic types
+ Box::new(read_parcel_interface!(Vec<i8>)),
+ Box::new(read_parcel_interface!(Vec<i32>)),
+ Box::new(read_parcel_interface!(Vec<i64>)),
+ Box::new(read_parcel_interface!(Vec<f32>)),
+ Box::new(read_parcel_interface!(Vec<f64>)),
+ Box::new(read_parcel_interface!(Vec<u16>)),
+ Box::new(read_parcel_interface!(Vec<u32>)),
+ Box::new(read_parcel_interface!(Vec<u64>)),
+ Box::new(read_parcel_interface!(Vec<String>)),
+ Box::new(read_parcel_interface!(Option<Vec<i8>>)),
+ Box::new(read_parcel_interface!(Option<Vec<i32>>)),
+ Box::new(read_parcel_interface!(Option<Vec<i64>>)),
+ Box::new(read_parcel_interface!(Option<Vec<f32>>)),
+ Box::new(read_parcel_interface!(Option<Vec<f64>>)),
+ Box::new(read_parcel_interface!(Option<Vec<u16>>)),
+ Box::new(read_parcel_interface!(Option<Vec<u32>>)),
+ Box::new(read_parcel_interface!(Option<Vec<u64>>)),
+ Box::new(read_parcel_interface!(Option<Vec<String>>)),
+ Box::new(read_parcel_interface!(ParcelFileDescriptor)),
+ Box::new(read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>)),
+ Box::new(read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>)),
+ Box::new(read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>)),
+ Box::new(read_parcel_interface!(SpIBinder)),
+ Box::new(read_parcel_interface!(Vec<Option<SpIBinder>>)),
+ Box::new(read_parcel_interface!(Option<Vec<SpIBinder>>)),
+ Box::new(read_parcel_interface!(Option<Vec<Option<SpIBinder>>>)),
+ Box::new(read_parcel_interface!(SomeParcelable)),
+ Box::new(read_parcel_interface!(Vec<Option<SomeParcelable>>)),
+ Box::new(read_parcel_interface!(Option<Vec<SomeParcelable>>)),
+ Box::new(read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>)),
+ // Fuzz read_from_parcel for AIDL generated parcelables
+ Box::new(|parcel| {
+ let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default();
+ match empty_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => {
+ println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e)
+ }
+ }
+ }),
+ Box::new(|parcel| {
+ let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default();
+ match single_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => println!(
+ "SingleDataParcelable: error occurred while reading from a parcel: {:?}",
+ e
+ ),
+ }
+ }),
+ Box::new(|parcel| {
+ let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default();
+ match generic_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => println!(
+ "GenericDataParcelable: error occurred while reading from a parcel: {:?}",
+ e
+ ),
+ }
+ }),
+ ];
+
+ read_funcs
+}
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 3904e1d..61a2412 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -20,6 +20,9 @@
java: {
enabled: false,
},
+ rust: {
+ enabled: true,
+ },
},
}