From 35653ecaa77ce701b0146a43a142da82a6733b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 15 Aug 2022 09:14:37 +0200 Subject: [PATCH] Add fuzzing --- fuzz/.gitignore | 3 + fuzz/Cargo.toml | 27 ++++++ fuzz/fuzz_targets/fuzz_target_1.rs | 131 +++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/fuzz_target_1.rs diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..a092511 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,3 @@ +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..c4353d2 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "apdu-dispatch-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +arbitrary = { version = "1", features = ["derive"] } +interchange = "0.2.0" + +[dependencies.apdu-dispatch] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "fuzz_target_1" +path = "fuzz_targets/fuzz_target_1.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/fuzz_target_1.rs b/fuzz/fuzz_targets/fuzz_target_1.rs new file mode 100644 index 0000000..81f3727 --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_target_1.rs @@ -0,0 +1,131 @@ +#![no_main] +use apdu_dispatch::app::Result as AppResult; +use apdu_dispatch::{dispatch::Interface, interchanges, iso7816, App}; +use arbitrary::{Arbitrary, Unstructured}; +use interchange::Interchange; +use libfuzzer_sys::fuzz_target; + +use std::convert::TryFrom; + +#[derive(Debug)] +struct StatusWrapper(iso7816::Status); + +impl<'a> Arbitrary<'a> for StatusWrapper { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let (tag1, tag2) = u.arbitrary()?; + Ok(StatusWrapper( + iso7816::Status::try_from((tag1, tag2)) + .map_err(|_| arbitrary::Error::IncorrectFormat)?, + )) + } +} + +#[derive(Debug, Arbitrary)] +struct Input { + apps: Vec, + apdus: Vec<(Vec, bool)>, +} + +#[derive(Debug, Arbitrary)] +struct FuzzApp { + responses: Vec<(Vec, Option)>, +} + +struct FuzzAppImpl { + id: usize, + responses: Vec<(Vec, Option)>, + count: usize, +} + +impl FuzzAppImpl { + fn new(id: usize, app: FuzzApp) -> Self { + FuzzAppImpl { + responses: app + .responses + .into_iter() + .map(|(data, maybe_status)| (data, maybe_status.map(|s| s.0))) + .collect(), + id, + count: 0, + } + } +} + +impl iso7816::App for FuzzAppImpl { + fn aid(&self) -> iso7816::Aid { + let [b1, b2] = (self.id as u16).to_be_bytes(); + iso7816::Aid::new(&[0x0Au8, 1, 0, b1, b2]) + } +} + +impl App<{ apdu_dispatch::command::SIZE }, { apdu_dispatch::response::SIZE }> for FuzzAppImpl { + fn select( + &mut self, + _apdu: &apdu_dispatch::Command, + _reply: &mut apdu_dispatch::response::Data, + ) -> AppResult { + Ok(Default::default()) + } + + fn deselect(&mut self) {} + + fn call( + &mut self, + _: Interface, + _apdu: &apdu_dispatch::Command, + reply: &mut apdu_dispatch::response::Data, + ) -> AppResult { + let (ref data, status) = &self.responses[self.count]; + reply.extend_from_slice(&data).ok(); + self.count += 1; + self.count = self.count % self.responses.len(); + match status { + Some(s) => Err(s.clone()), + None => Ok(()), + } + } +} + +fuzz_target!(|input: Input| { + unsafe { interchanges::Contact::reset_claims() }; + unsafe { interchanges::Contactless::reset_claims() }; + let mut apps: Vec<_> = input + .apps + .into_iter() + .enumerate() + .map(|(idx, app)| FuzzAppImpl::new(idx, app)) + .collect(); + let mut dyn_apps: Vec<_> = apps + .iter_mut() + .map(|s| (s as &mut dyn apdu_dispatch::App<7609, 7609>)) + .collect(); + + let (mut contact_requester, contact_responder) = + interchanges::Contact::claim().expect("could not setup ccid ApduInterchange"); + + let (mut contactless_requester, contactless_responder) = + interchanges::Contactless::claim().expect("could not setup iso14443 ApduInterchange"); + + let mut apdu_dispatch = + apdu_dispatch::dispatch::ApduDispatch::new(contact_responder, contactless_responder); + + for (mut apdu, requester) in input.apdus { + apdu.truncate(interchanges::SIZE); + if requester { + contact_requester + .request(&interchanges::Data::from_slice(&apdu).unwrap()) + .expect("could not deposit command"); + } else { + contactless_requester + .request(&interchanges::Data::from_slice(&apdu).unwrap()) + .expect("could not deposit command"); + } + apdu_dispatch.poll(&mut dyn_apps); + + if requester { + contact_requester.take_response().unwrap(); + } else { + contactless_requester.take_response().unwrap(); + } + } +});