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,
+        },
     },
 }