diff --git a/.github/workflows/rust-fuzz.yml b/.github/workflows/rust-fuzz.yml new file mode 100644 index 0000000..286a01b --- /dev/null +++ b/.github/workflows/rust-fuzz.yml @@ -0,0 +1,38 @@ +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +name: Quick Fuzzing + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + fuzzing: + name: Fuzzing + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install nightly toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + + - name: Install cargo-fuzz + uses: actions-rs/cargo@v1 + with: + command: 'install' + args: 'cargo-fuzz' + + - name: Run cargo-fuzz + uses: actions-rs/cargo@v1 + with: + command: 'fuzz' + args: 'run parse_session -- -max_total_time=20' + diff --git a/examples/parse.rs b/examples/parse.rs new file mode 100644 index 0000000..846a7c1 --- /dev/null +++ b/examples/parse.rs @@ -0,0 +1,40 @@ +use std::io::prelude::*; +use std::{env, fs, process}; + +fn main() -> process::ExitCode { + let mut args = env::args(); + let _ = args.next().unwrap(); + let Some(file) = args.next() else { + eprintln!("Usage: parse [file.sdp]"); + return process::ExitCode::FAILURE; + }; + + let mut file = match fs::File::open(file) { + Ok(file) => file, + Err(err) => { + eprintln!("Failed to open file: {err}"); + return process::ExitCode::FAILURE; + } + }; + + let mut content = Vec::new(); + match file.read_to_end(&mut content) { + Ok(_) => {} + Err(err) => { + eprintln!("Failed to read file: {err}"); + return process::ExitCode::FAILURE; + } + }; + + let sdp = match sdp_types::Session::parse(&content) { + Ok(sdp) => sdp, + Err(err) => { + eprintln!("Failed to parse SDP: {err}"); + return process::ExitCode::FAILURE; + } + }; + + println!("{sdp:#?}"); + + process::ExitCode::SUCCESS +} diff --git a/fuzz/corpus/parse_session/crypto b/fuzz/corpus/parse_session/crypto new file mode 100644 index 0000000..0aa1cda --- /dev/null +++ b/fuzz/corpus/parse_session/crypto @@ -0,0 +1,14 @@ +v=0 +o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 +s=SDP Seminar +i=A Seminar on the session description protocol +u=http://www.example.com/seminars/sdp.pdf +e=j.doe@example.com (Jane Doe) +c=IN IP4 161.44.17.12/127 +t=2873397496 2873404696 +m=video 51372 RTP/SAVP 31 +a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32 +m=audio 49170 RTP/SAVP 0 +a=crypto:1 AES_CM_128_HMAC_SHA1_32 inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 +m=application 32416 udp wb +a=orient:portrait diff --git a/fuzz/corpus/parse_session/simple b/fuzz/corpus/parse_session/simple new file mode 100644 index 0000000..8a6d409 --- /dev/null +++ b/fuzz/corpus/parse_session/simple @@ -0,0 +1,12 @@ +v=0 +o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 +s=SDP Seminar +i=A Seminar on the session description protocol +u=http://www.example.com/seminars/sdp.pdf +e=j.doe@example.com (Jane Doe) +c=IN IP4 224.2.17.12/127 +t=2873397496 2873404696 +a=recvonly +m=audio 49170 RTP/AVP 0 +m=video 51372 RTP/AVP 99 +a=rtpmap:99 h263-1998/90000 diff --git a/fuzz/fuzz_targets/parse_session.rs b/fuzz/fuzz_targets/parse_session.rs index b740cc9..c516567 100644 --- a/fuzz/fuzz_targets/parse_session.rs +++ b/fuzz/fuzz_targets/parse_session.rs @@ -2,5 +2,11 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - let _ = sdp_types::Session::parse(data); + let session = sdp_types::Session::parse(data); + + if let Ok (s) = session { + // serialize the parsed session + let mut written = vec![]; + s.write(&mut written).unwrap(); + }; }); diff --git a/src/parser.rs b/src/parser.rs index 0073f47..e3d92e4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -370,10 +370,10 @@ impl Media { ) -> Result, ParserError> { let media = match lines.next()? { None => return Ok(None), - Some(line) => { - assert_eq!(line.key, b'm'); - Media::parse_m_line(&line)? - } + Some(line) => match line.key { + b'm' => Media::parse_m_line(&line)?, + _ => return Err(ParserError::InvalidLineFormat(line.n, "line key not m")), + }, }; // As with Session::parse, be more permissive about order than RFC 8866.