From 5182d12488137cc89d490bc7ddaeefbd3d57f7c4 Mon Sep 17 00:00:00 2001 From: Mauro D Date: Mon, 3 Apr 2023 16:23:30 +0000 Subject: [PATCH] Added fuzzing. --- fuzz/.gitignore | 3 ++ fuzz/Cargo.toml | 25 +++++++++++ fuzz/fuzz_targets/smtp_proto.rs | 80 +++++++++++++++++++++++++++++++++ src/request/receiver.rs | 2 +- 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/smtp_proto.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..7a78076 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "smtp-proto-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.smtp-proto] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "smtp_proto" +path = "fuzz_targets/smtp_proto.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/smtp_proto.rs b/fuzz/fuzz_targets/smtp_proto.rs new file mode 100644 index 0000000..a1577ce --- /dev/null +++ b/fuzz/fuzz_targets/smtp_proto.rs @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020-2023, Stalwart Labs Ltd. + * + * This file is part of the Stalwart SMTP protocol parser. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * in the LICENSE file at the top-level directory of this distribution. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * You can be released from the requirements of the AGPLv3 license by + * purchasing a commercial license. Please contact licensing@stalw.art + * for more details. +*/ + +#![no_main] +use libfuzzer_sys::fuzz_target; + +use smtp_proto::{ + request::{ + parser::Rfc5321Parser, + receiver::{ + BdatReceiver, DataReceiver, DummyDataReceiver, DummyLineReceiver, LineReceiver, + RequestReceiver, + }, + }, + response::parser::ResponseReceiver, + EhloResponse, Request, +}; + +static RFC5321_ALPHABET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz:=-<>,; \r\n"; + +fuzz_target!(|data: &[u8]| { + let data_rfc5321 = into_alphabet(data, RFC5321_ALPHABET); + + for bytes in [data, &data_rfc5321] { + let _ = Request::parse(&mut bytes.iter()); + let _ = RequestReceiver::default().ingest(&mut bytes.iter(), &[]); + let _ = DataReceiver::new().ingest(&mut bytes.iter(), &mut vec![]); + let _ = BdatReceiver::new(bytes.len(), true).ingest(&mut bytes.iter(), &mut vec![]); + let _ = BdatReceiver::new(bytes.len(), false).ingest(&mut bytes.iter(), &mut vec![]); + let _ = DummyDataReceiver::new_bdat(bytes.len()).ingest(&mut bytes.iter()); + let _ = DummyDataReceiver::new_data(&DataReceiver::new()).ingest(&mut bytes.iter()); + let _ = LineReceiver::new(()).ingest(&mut bytes.iter()); + let _ = DummyLineReceiver::default().ingest(&mut bytes.iter()); + let _ = ResponseReceiver::default().parse(&mut bytes.iter()); + let _ = EhloResponse::::parse(&mut bytes.iter()); + + let _ = Rfc5321Parser::new(&mut bytes.iter()).hashed_value(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).hashed_value_long(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).address(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).string(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).text(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).xtext(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).seek_char(0); + let _ = Rfc5321Parser::new(&mut bytes.iter()).seek_lf(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).next_char(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).read_char(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).size(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).integer(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).timestamp(); + let _ = Rfc5321Parser::new(&mut bytes.iter()).mail_from_parameters(String::new()); + let _ = Rfc5321Parser::new(&mut bytes.iter()).rcpt_to_parameters(String::new()); + let _ = Rfc5321Parser::new(&mut bytes.iter()).mechanism(); + } +}); + +fn into_alphabet(data: &[u8], alphabet: &[u8]) -> Vec { + data.iter() + .map(|&byte| alphabet[byte as usize % alphabet.len()]) + .collect() +} diff --git a/src/request/receiver.rs b/src/request/receiver.rs index 7207516..fb8e040 100644 --- a/src/request/receiver.rs +++ b/src/request/receiver.rs @@ -71,7 +71,7 @@ impl RequestReceiver { Err(Error::NeedsMoreData { bytes_left }) => { if bytes_left > 0 { if bytes_left < MAX_LINE_LENGTH { - self.buf = buf[buf.len() - bytes_left..].to_vec(); + self.buf = buf[buf.len().saturating_sub(bytes_left)..].to_vec(); } else { return Err(Error::ResponseTooLong); }