add a fuzzer
This commit is contained in:
parent
e52ce4a61d
commit
bb9cb386b6
9 changed files with 4364 additions and 33 deletions
|
@ -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
4
fuzz/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
target
|
||||||
|
corpus
|
||||||
|
artifacts
|
||||||
|
coverage
|
4249
fuzz/Cargo.lock
generated
Normal file
4249
fuzz/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
27
fuzz/Cargo.toml
Normal file
27
fuzz/Cargo.toml
Normal 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
48
fuzz/fuzz_targets/dav.rs
Normal 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);
|
||||||
|
})
|
||||||
|
});
|
|
@ -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;
|
||||||
|
|
|
@ -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
19
src/lib.rs
Normal 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;
|
26
src/main.rs
26
src/main.rs
|
@ -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)]
|
||||||
|
|
Loading…
Reference in a new issue