add a fuzzer

This commit is contained in:
Quentin 2024-03-07 15:45:05 +01:00
parent e52ce4a61d
commit bb9cb386b6
Signed by: quentin
GPG key ID: E9602264D639FF68
9 changed files with 4364 additions and 33 deletions

View file

@ -6,6 +6,10 @@ edition = "2021"
license = "EUPL-1.2" license = "EUPL-1.2"
description = "A robust email server" description = "A robust email server"
[lib]
name = "aerogramme"
path = "src/lib.rs"
[dependencies] [dependencies]
# async runtime # async runtime
tokio = { version = "1.18", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] } tokio = { version = "1.18", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] }

4
fuzz/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
target
corpus
artifacts
coverage

4249
fuzz/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

27
fuzz/Cargo.toml Normal file
View file

@ -0,0 +1,27 @@
[package]
name = "aerogramme-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
tokio = { version = "1.18", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] }
quick-xml = { version = "0.31", features = ["async-tokio"] }
[dependencies.aerogramme]
path = ".."
[patch.crates-io]
imap-types = { git = "https://github.com/superboum/imap-codec", branch = "custom/aerogramme" }
imap-codec = { git = "https://github.com/superboum/imap-codec", branch = "custom/aerogramme" }
[[bin]]
name = "dav"
path = "fuzz_targets/dav.rs"
test = false
doc = false
bench = false

48
fuzz/fuzz_targets/dav.rs Normal file
View file

@ -0,0 +1,48 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use aerogramme::dav::{types, realization, xml};
use quick_xml::reader::NsReader;
use tokio::runtime::Runtime;
use tokio::io::AsyncWriteExt;
async fn serialize(elem: &impl xml::QWrite) -> Vec<u8> {
let mut buffer = Vec::new();
let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
let q = quick_xml::writer::Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()) ];
let mut writer = xml::Writer { q, ns_to_apply };
elem.qwrite(&mut writer).await.expect("xml serialization");
tokio_buffer.flush().await.expect("tokio buffer flush");
return buffer
}
type Object = types::Multistatus<realization::Core, types::PropValue<realization::Core>>;
fuzz_target!(|data: &[u8]| {
let rt = Runtime::new().expect("tokio runtime initialization");
rt.block_on(async {
// 1. Setup fuzzing by finding an input that seems correct, do not crash yet then.
let mut rdr = match xml::Reader::new(NsReader::from_reader(data)).await {
Err(_) => return,
Ok(r) => r,
};
let reference = match rdr.find::<Object>().await {
Err(_) => return,
Ok(m) => m,
};
// 2. Re-serialize the input
let my_serialization = serialize(&reference).await;
// 3. De-serialize my serialization
let mut rdr2 = xml::Reader::new(NsReader::from_reader(my_serialization.as_slice())).await.expect("XML Reader init");
let comparison = rdr2.find::<Object>().await.expect("Deserialize again");
// 4. Both the first decoding and last decoding must be identical
assert_eq!(reference, comparison);
})
});

View file

@ -1,11 +1,11 @@
// utils // utils
mod error; pub mod error;
mod xml; pub mod xml;
// webdav // webdav
mod types; pub mod types;
mod encoder; pub mod encoder;
mod decoder; pub mod decoder;
// calendar // calendar
mod caltypes; mod caltypes;
@ -17,7 +17,7 @@ mod acltypes;
mod versioningtypes; mod versioningtypes;
// final type // final type
mod realization; pub mod realization;
use std::net::SocketAddr; use std::net::SocketAddr;

View file

@ -12,7 +12,7 @@ pub const CARD_URN: &[u8] = b"urn:ietf:params:xml:ns:carddav";
// Async traits // Async traits
pub trait IWrite = AsyncWrite + Unpin; pub trait IWrite = AsyncWrite + Unpin;
pub trait IRead = AsyncBufRead + Unpin + 'static; pub trait IRead = AsyncBufRead + Unpin;
// Serialization/Deserialization traits // Serialization/Deserialization traits
pub trait QWrite { pub trait QWrite {
@ -78,7 +78,7 @@ impl<T: IRead> Reader<T> {
/// skip a node at current level /// skip a node at current level
/// I would like to make this one private but not ready /// I would like to make this one private but not ready
pub async fn skip(&mut self) -> Result<Event<'static>, ParsingError> { pub async fn skip(&mut self) -> Result<Event<'static>, ParsingError> {
println!("skipping inside node {:?}", self.parents.last()); //println!("skipping inside node {:?}", self.parents.last());
match &self.cur { match &self.cur {
Event::Start(b) => { Event::Start(b) => {
let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?; let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?;
@ -235,7 +235,7 @@ impl<T: IRead> Reader<T> {
_ => return Err(ParsingError::Recoverable), _ => return Err(ParsingError::Recoverable),
}; };
println!("open tag {:?}", evt); //println!("open tag {:?}", evt);
self.parents.push(evt.clone()); self.parents.push(evt.clone());
Ok(evt) Ok(evt)
} }
@ -250,7 +250,7 @@ impl<T: IRead> Reader<T> {
// find stop tag // find stop tag
pub async fn close(&mut self) -> Result<Event<'static>, ParsingError> { pub async fn close(&mut self) -> Result<Event<'static>, ParsingError> {
println!("close tag {:?}", self.parents.last()); //println!("close tag {:?}", self.parents.last());
// Handle the empty case // Handle the empty case
if !self.parent_has_child() { if !self.parent_has_child() {

19
src/lib.rs Normal file
View file

@ -0,0 +1,19 @@
#![feature(type_alias_impl_trait)]
#![feature(async_fn_in_trait)]
#![feature(async_closure)]
#![feature(trait_alias)]
pub mod auth;
pub mod bayou;
pub mod config;
pub mod cryptoblob;
pub mod dav;
pub mod imap;
pub mod k2v_util;
pub mod lmtp;
pub mod login;
pub mod mail;
pub mod server;
pub mod storage;
pub mod timestamp;
pub mod user;

View file

@ -1,23 +1,3 @@
#![feature(type_alias_impl_trait)]
#![feature(async_fn_in_trait)]
#![feature(async_closure)]
#![feature(trait_alias)]
mod auth;
mod bayou;
mod config;
mod cryptoblob;
mod dav;
mod imap;
mod k2v_util;
mod lmtp;
mod login;
mod mail;
mod server;
mod storage;
mod timestamp;
mod user;
use std::io::Read; use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
@ -25,9 +5,9 @@ use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use nix::{sys::signal, unistd::Pid}; use nix::{sys::signal, unistd::Pid};
use config::*; use aerogramme::config::*;
use login::{static_provider::*, *}; use aerogramme::login::{static_provider::*, *};
use server::Server; use aerogramme::server::Server;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]