From cd564105a7dbd5e4b7aed68caec91fa2055a71ee Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sat, 9 Nov 2019 22:47:32 +0100 Subject: [PATCH 1/9] [rs] Refactor parser modules --- rs/src/{parsers => complete}/button.rs | 8 ++-- rs/src/{parsers => complete}/display.rs | 4 +- rs/src/{parsers => complete}/gradient.rs | 4 +- rs/src/{parsers => complete}/image.rs | 2 +- rs/src/complete/mod.rs | 15 ++++++ rs/src/{parsers => complete}/morph_shape.rs | 8 ++-- rs/src/{parsers => complete}/shape.rs | 13 +++-- rs/src/{parsers => complete}/sound.rs | 2 +- rs/src/complete/tag.rs | 47 +++++++++---------- rs/src/{parsers => complete}/text.rs | 6 +-- rs/src/{parsers => complete}/video.rs | 2 +- rs/src/lib.rs | 43 ++++------------- .../basic_data_types.rs | 1 - rs/src/streaming/mod.rs | 4 ++ rs/src/streaming/movie.rs | 2 +- 15 files changed, 75 insertions(+), 86 deletions(-) rename rs/src/{parsers => complete}/button.rs (94%) rename rs/src/{parsers => complete}/display.rs (98%) rename rs/src/{parsers => complete}/gradient.rs (95%) rename rs/src/{parsers => complete}/image.rs (97%) create mode 100644 rs/src/complete/mod.rs rename rs/src/{parsers => complete}/morph_shape.rs (98%) rename rs/src/{parsers => complete}/shape.rs (98%) rename rs/src/{parsers => complete}/sound.rs (96%) rename rs/src/{parsers => complete}/text.rs (98%) rename rs/src/{parsers => complete}/video.rs (95%) rename rs/src/{parsers => streaming}/basic_data_types.rs (99%) create mode 100644 rs/src/streaming/mod.rs diff --git a/rs/src/parsers/button.rs b/rs/src/complete/button.rs similarity index 94% rename from rs/src/parsers/button.rs rename to rs/src/complete/button.rs index cc83468..9690538 100644 --- a/rs/src/parsers/button.rs +++ b/rs/src/complete/button.rs @@ -1,7 +1,7 @@ -use crate::parsers::basic_data_types::{parse_color_transform_with_alpha, parse_matrix}; -use crate::parsers::display::{parse_blend_mode, parse_filter_list}; -use crate::parsers::sound::parse_sound_info; -use nom::number::streaming::{le_u16 as parse_le_u16, le_u8 as parse_u8}; +use crate::complete::display::{parse_blend_mode, parse_filter_list}; +use crate::complete::sound::parse_sound_info; +use crate::streaming::basic_data_types::{parse_color_transform_with_alpha, parse_matrix}; +use nom::number::complete::{le_u16 as parse_le_u16, le_u8 as parse_u8}; use nom::IResult as NomResult; use swf_tree as ast; diff --git a/rs/src/parsers/display.rs b/rs/src/complete/display.rs similarity index 98% rename from rs/src/parsers/display.rs rename to rs/src/complete/display.rs index 4ce2d8a..63081bd 100644 --- a/rs/src/parsers/display.rs +++ b/rs/src/complete/display.rs @@ -1,5 +1,5 @@ -use crate::parsers::basic_data_types::{parse_le_fixed16_p16, parse_le_fixed8_p8, parse_straight_s_rgba8}; -use nom::number::streaming::{ +use crate::streaming::basic_data_types::{parse_le_fixed16_p16, parse_le_fixed8_p8, parse_straight_s_rgba8}; +use nom::number::complete::{ le_f32 as parse_le_f32, le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8, }; use nom::IResult as NomResult; diff --git a/rs/src/parsers/gradient.rs b/rs/src/complete/gradient.rs similarity index 95% rename from rs/src/parsers/gradient.rs rename to rs/src/complete/gradient.rs index 9bcc96c..9d75972 100644 --- a/rs/src/parsers/gradient.rs +++ b/rs/src/complete/gradient.rs @@ -1,5 +1,5 @@ -use crate::parsers::basic_data_types::{parse_s_rgb8, parse_straight_s_rgba8}; -use nom::number::streaming::le_u8 as parse_u8; +use crate::streaming::basic_data_types::{parse_s_rgb8, parse_straight_s_rgba8}; +use nom::number::complete::le_u8 as parse_u8; use nom::IResult as NomResult; use swf_tree as ast; diff --git a/rs/src/parsers/image.rs b/rs/src/complete/image.rs similarity index 97% rename from rs/src/parsers/image.rs rename to rs/src/complete/image.rs index b6fefb4..539b242 100644 --- a/rs/src/parsers/image.rs +++ b/rs/src/complete/image.rs @@ -1,4 +1,4 @@ -use nom::number::streaming::{be_u16 as parse_be_u16, be_u32 as parse_be_u32, le_u32 as parse_le_u32}; +use nom::number::complete::{be_u16 as parse_be_u16, be_u32 as parse_be_u32, le_u32 as parse_le_u32}; pub struct ImageDimensions { pub width: usize, diff --git a/rs/src/complete/mod.rs b/rs/src/complete/mod.rs new file mode 100644 index 0000000..8b4778b --- /dev/null +++ b/rs/src/complete/mod.rs @@ -0,0 +1,15 @@ +pub(crate) mod button; +pub(crate) mod display; +pub(crate) mod gradient; +pub(crate) mod image; +pub(crate) mod morph_shape; +pub(crate) mod movie; +pub(crate) mod shape; +pub(crate) mod sound; +pub(crate) mod tag; +pub(crate) mod text; +pub(crate) mod video; + +pub use movie::parse_swf; +pub use movie::SwfParseError; +pub use tag::parse_tag; diff --git a/rs/src/parsers/morph_shape.rs b/rs/src/complete/morph_shape.rs similarity index 98% rename from rs/src/parsers/morph_shape.rs rename to rs/src/complete/morph_shape.rs index a1bf2f1..99318c9 100644 --- a/rs/src/parsers/morph_shape.rs +++ b/rs/src/complete/morph_shape.rs @@ -1,10 +1,10 @@ -use crate::parsers::basic_data_types::{ +use crate::complete::gradient::parse_morph_gradient; +use crate::complete::shape::{parse_curved_edge_bits, parse_list_length, parse_straight_edge_bits, StyleBits}; +use crate::streaming::basic_data_types::{ do_parse_u16_bits, do_parse_u32_bits, parse_bool_bits, parse_i32_bits, parse_le_fixed8_p8, parse_matrix, parse_straight_s_rgba8, parse_u16_bits, }; -use crate::parsers::gradient::parse_morph_gradient; -use crate::parsers::shape::{parse_curved_edge_bits, parse_list_length, parse_straight_edge_bits, StyleBits}; -use nom::number::streaming::{le_u16 as parse_le_u16, le_u8 as parse_u8}; +use nom::number::complete::{le_u16 as parse_le_u16, le_u8 as parse_u8}; use nom::{IResult as NomResult, Needed}; use std::convert::TryFrom; use swf_tree as ast; diff --git a/rs/src/parsers/shape.rs b/rs/src/complete/shape.rs similarity index 98% rename from rs/src/parsers/shape.rs rename to rs/src/complete/shape.rs index 0a917ca..519d464 100644 --- a/rs/src/parsers/shape.rs +++ b/rs/src/complete/shape.rs @@ -1,13 +1,12 @@ -use nom::number::streaming::{le_u16 as parse_le_u16, le_u8 as parse_u8}; -use nom::{IResult as NomResult, Needed}; -use std::convert::TryFrom; -use swf_tree as ast; - -use crate::parsers::basic_data_types::{ +use crate::complete::gradient::parse_gradient; +use crate::streaming::basic_data_types::{ do_parse_i32_bits, do_parse_u16_bits, do_parse_u32_bits, parse_bool_bits, parse_i32_bits, parse_le_fixed8_p8, parse_matrix, parse_s_rgb8, parse_straight_s_rgba8, parse_u16_bits, }; -use crate::parsers::gradient::parse_gradient; +use nom::number::complete::{le_u16 as parse_le_u16, le_u8 as parse_u8}; +use nom::{IResult as NomResult, Needed}; +use std::convert::TryFrom; +use swf_tree as ast; #[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)] pub enum ShapeVersion { diff --git a/rs/src/parsers/sound.rs b/rs/src/complete/sound.rs similarity index 96% rename from rs/src/parsers/sound.rs rename to rs/src/complete/sound.rs index 0556458..3ca6fab 100644 --- a/rs/src/parsers/sound.rs +++ b/rs/src/complete/sound.rs @@ -1,4 +1,4 @@ -use nom::number::streaming::{le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8}; +use nom::number::complete::{le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8}; use nom::IResult as NomResult; use swf_tree as ast; diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index f2ba56e..cd2299b 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -1,37 +1,36 @@ -use nom::number::complete::{ - le_f32 as parse_le_f32, le_i16 as parse_le_i16, le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8, -}; -use nom::IResult as NomResult; -use std::convert::TryFrom; -use swf_tree as ast; -use swf_tree::{ButtonCondAction, Glyph}; - -use crate::parsers::basic_data_types::{ - parse_block_c_string, parse_c_string, parse_color_transform, parse_color_transform_with_alpha, parse_language_code, - parse_leb128_u32, parse_matrix, parse_named_id, parse_rect, parse_s_rgb8, parse_straight_s_rgba8, -}; -use crate::parsers::button::{ +use crate::complete::button::{ parse_button2_cond_action_string, parse_button_record_string, parse_button_sound, ButtonVersion, }; -use crate::parsers::display::{parse_blend_mode, parse_clip_actions_string, parse_filter_list}; -use crate::parsers::image::get_gif_image_dimensions; -use crate::parsers::image::get_png_image_dimensions; -use crate::parsers::image::GIF_START; -use crate::parsers::image::PNG_START; -use crate::parsers::image::{get_jpeg_image_dimensions, test_image_start, ERRONEOUS_JPEG_START, JPEG_START}; -use crate::parsers::morph_shape::{parse_morph_shape, MorphShapeVersion}; -use crate::parsers::shape::{parse_glyph, parse_shape, ShapeVersion}; -use crate::parsers::sound::{ +use crate::complete::display::{parse_blend_mode, parse_clip_actions_string, parse_filter_list}; +use crate::complete::image::get_gif_image_dimensions; +use crate::complete::image::get_png_image_dimensions; +use crate::complete::image::GIF_START; +use crate::complete::image::PNG_START; +use crate::complete::image::{get_jpeg_image_dimensions, test_image_start, ERRONEOUS_JPEG_START, JPEG_START}; +use crate::complete::morph_shape::{parse_morph_shape, MorphShapeVersion}; +use crate::complete::shape::{parse_glyph, parse_shape, ShapeVersion}; +use crate::complete::sound::{ audio_coding_format_from_id, is_uncompressed_audio_coding_format, parse_sound_info, sound_rate_from_id, }; -use crate::parsers::text::{ +use crate::complete::text::{ grid_fitting_from_code, parse_csm_table_hint_bits, parse_font_alignment_zone, parse_font_layout, parse_offset_glyphs, parse_text_alignment, parse_text_record_string, text_renderer_from_code, FontInfoVersion, FontVersion, TextVersion, }; -use crate::parsers::video::{parse_videoc_codec, video_deblocking_from_id}; +use crate::complete::video::{parse_videoc_codec, video_deblocking_from_id}; +use crate::streaming::basic_data_types::{ + parse_block_c_string, parse_c_string, parse_color_transform, parse_color_transform_with_alpha, parse_language_code, + parse_leb128_u32, parse_matrix, parse_named_id, parse_rect, parse_s_rgb8, parse_straight_s_rgba8, +}; use crate::streaming::movie::parse_tag_block_string; use crate::streaming::tag::StreamingTagError; +use nom::number::complete::{ + le_f32 as parse_le_f32, le_i16 as parse_le_i16, le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8, +}; +use nom::IResult as NomResult; +use std::convert::TryFrom; +use swf_tree as ast; use swf_tree::text::FontAlignmentZone; +use swf_tree::{ButtonCondAction, Glyph}; /// Parses that tag at the start of `input`. /// diff --git a/rs/src/parsers/text.rs b/rs/src/complete/text.rs similarity index 98% rename from rs/src/parsers/text.rs rename to rs/src/complete/text.rs index b7fbff6..9d4946b 100644 --- a/rs/src/parsers/text.rs +++ b/rs/src/complete/text.rs @@ -1,8 +1,8 @@ -use crate::parsers::basic_data_types::{ +use crate::complete::shape::parse_glyph; +use crate::streaming::basic_data_types::{ do_parse_u32_bits, parse_i32_bits, parse_le_f16, parse_rect, parse_s_rgb8, parse_straight_s_rgba8, parse_u32_bits, }; -use crate::parsers::shape::parse_glyph; -use nom::number::streaming::{ +use nom::number::complete::{ le_i16 as parse_le_i16, le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8, }; use nom::IResult as NomResult; diff --git a/rs/src/parsers/video.rs b/rs/src/complete/video.rs similarity index 95% rename from rs/src/parsers/video.rs rename to rs/src/complete/video.rs index d6417b6..08e0186 100644 --- a/rs/src/parsers/video.rs +++ b/rs/src/complete/video.rs @@ -1,4 +1,4 @@ -use nom::number::streaming::le_u8 as parse_u8; +use nom::number::complete::le_u8 as parse_u8; use nom::IResult as NomResult; use swf_tree as ast; diff --git a/rs/src/lib.rs b/rs/src/lib.rs index 6731e7f..c656e7d 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -1,35 +1,8 @@ -extern crate inflate; -extern crate nom; -extern crate num_traits; -extern crate swf_fixed; -extern crate swf_tree; - +pub mod parsers {} +pub mod complete; mod stream_buffer; - -pub mod parsers { - pub mod basic_data_types; - pub mod button; - pub mod display; - pub mod gradient; - pub mod image; - pub mod morph_shape; - pub mod shape; - pub mod sound; - pub mod text; - pub mod video; -} -pub mod complete { - pub(crate) mod movie; - pub(crate) mod tag; - pub use movie::parse_swf; - pub use movie::SwfParseError; - pub use tag::parse_tag; -} -pub mod streaming { - pub mod movie; - pub mod parser; - pub mod tag; -} +pub mod streaming; +pub use swf_tree; #[cfg(test)] mod tests { @@ -170,7 +143,7 @@ mod tests { }; } - use crate::parsers::basic_data_types::parse_le_f16; + use crate::streaming::basic_data_types::parse_le_f16; test_various_parser_is_impl!(test_parse_le_f16, "../tests/various/float16-le/*/", parse_le_f16, f32); use crate::streaming::movie::parse_header; @@ -181,11 +154,11 @@ mod tests { } test_various_parser_impl!(test_parse_header, "../tests/various/header/*/", parse_header34, Header); - use crate::parsers::basic_data_types::parse_matrix; + use crate::streaming::basic_data_types::parse_matrix; use swf_tree::Matrix; test_various_parser_impl!(test_parse_matrix, "../tests/various/matrix/*/", parse_matrix, Matrix); - use crate::parsers::basic_data_types::parse_rect; + use crate::streaming::basic_data_types::parse_rect; use swf_tree::Rect; test_various_parser_impl!(test_parse_rect, "../tests/various/rect/*/", parse_rect, Rect); @@ -198,7 +171,7 @@ mod tests { SwfSignature ); - use crate::parsers::basic_data_types::parse_leb128_u32; + use crate::streaming::basic_data_types::parse_leb128_u32; test_various_parser_impl!( test_parse_leb128_u32, "../tests/various/uint32-leb128/*/", diff --git a/rs/src/parsers/basic_data_types.rs b/rs/src/streaming/basic_data_types.rs similarity index 99% rename from rs/src/parsers/basic_data_types.rs rename to rs/src/streaming/basic_data_types.rs index 01b8218..95023dd 100644 --- a/rs/src/parsers/basic_data_types.rs +++ b/rs/src/streaming/basic_data_types.rs @@ -3,7 +3,6 @@ use nom::number::streaming::{ be_u16 as parse_be_u16, le_i16 as parse_le_i16, le_i32 as parse_le_i32, le_u16 as parse_le_u16, le_u8 as parse_u8, }; use nom::{IResult as NomResult, Needed}; -use std::f32; use swf_fixed::{Sfixed16P16, Sfixed8P8, Ufixed8P8}; use swf_tree as ast; use swf_tree::LanguageCode; diff --git a/rs/src/streaming/mod.rs b/rs/src/streaming/mod.rs new file mode 100644 index 0000000..b8fee53 --- /dev/null +++ b/rs/src/streaming/mod.rs @@ -0,0 +1,4 @@ +pub mod basic_data_types; +pub mod movie; +pub mod parser; +pub mod tag; diff --git a/rs/src/streaming/movie.rs b/rs/src/streaming/movie.rs index 9be567b..6e736a9 100644 --- a/rs/src/streaming/movie.rs +++ b/rs/src/streaming/movie.rs @@ -1,4 +1,4 @@ -use crate::parsers::basic_data_types::{parse_le_ufixed8_p8, parse_rect}; +use crate::streaming::basic_data_types::{parse_le_ufixed8_p8, parse_rect}; use crate::streaming::tag::parse_tag; use nom::number::streaming::{le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8}; use nom::{IResult as NomResult, Needed}; From 1459153c5087fe2b6f6c0dc77ceb4f7d09d81f5a Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 01:06:55 +0100 Subject: [PATCH 2/9] [rs] Refactor to prepare `tag` fuzzing --- rs/Cargo.lock | 9 +-------- rs/Cargo.toml | 6 +++--- rs/README.md | 2 ++ rs/fuzz/Cargo.lock | 14 +++++++------- rs/fuzz/Cargo.toml | 12 ++++++++---- rs/fuzz/fuzz_targets/swf.rs | 4 ++-- rs/fuzz/fuzz_targets/tag.rs | 9 +++++++++ rs/src/complete/tag.rs | 37 +++++++++++++++++++++++++++++++++++++ rs/src/lib.rs | 34 ---------------------------------- 9 files changed, 69 insertions(+), 58 deletions(-) create mode 100644 rs/fuzz/fuzz_targets/tag.rs diff --git a/rs/Cargo.lock b/rs/Cargo.lock index 8197196..75891b8 100644 --- a/rs/Cargo.lock +++ b/rs/Cargo.lock @@ -325,7 +325,7 @@ dependencies = [ "serde_json", "serde_json_v8", "swf-fixed", - "swf-tree 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-tree", "test-generator", ] @@ -339,13 +339,6 @@ dependencies = [ "swf-fixed", ] -[[package]] -name = "swf-tree" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50adec8b597acef58e7405d1aed5f3306f8628f848505925e568d070d2a1d902" -replace = "swf-tree 0.9.0 (git+https://github.com/open-flash/swf-tree.git?rev=524f19adc098d83900e94bbe693887a63b50786d)" - [[package]] name = "syn" version = "0.15.44" diff --git a/rs/Cargo.toml b/rs/Cargo.toml index e23998e..7c7e1ce 100644 --- a/rs/Cargo.toml +++ b/rs/Cargo.toml @@ -29,7 +29,7 @@ nom = "^5.0.0" num-traits = "^0.2.8" regex = "^1.1.8" serde_json = "^1.0.40" -swf-tree = "^0.9.0" +swf-tree = { git="https://github.com/open-flash/swf-tree.git", rev = "524f19adc098d83900e94bbe693887a63b50786d" } swf-fixed = "^0.1.4" [dev-dependencies] @@ -37,8 +37,8 @@ serde = "^1.0.94" serde_json_v8 = "^0.0.1" test-generator = "^0.2.2" -[replace] -"swf-tree:0.9.0" = { git="https://github.com/open-flash/swf-tree.git", rev = "524f19adc098d83900e94bbe693887a63b50786d" } +# [replace] +# "swf-tree:0.9.0" = { git="https://github.com/open-flash/swf-tree.git", rev = "524f19adc098d83900e94bbe693887a63b50786d" } # When testing larger files, increasing `opt-level` provides a significant speed-up. # [profile.test] diff --git a/rs/README.md b/rs/README.md index a320d26..5e87458 100644 --- a/rs/README.md +++ b/rs/README.md @@ -39,6 +39,8 @@ git submodule update --init --recursive --remote This library is a standard Cargo project. You can test your changes with `cargo test`. **The commands must be run from the `rs` directory.** +## Fuzzing + The Rust implementation supports fuzzing: ``` diff --git a/rs/fuzz/Cargo.lock b/rs/fuzz/Cargo.lock index 5275f99..d368c80 100644 --- a/rs/fuzz/Cargo.lock +++ b/rs/fuzz/Cargo.lock @@ -101,8 +101,8 @@ dependencies = [ [[package]] name = "libfuzzer-sys" -version = "0.1.0" -source = "git+https://github.com/rust-fuzz/libfuzzer-sys.git#4a413199b5cb1bbed6a1d157b2342b925f8464ac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arbitrary 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", @@ -271,21 +271,21 @@ dependencies = [ "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "swf-fixed 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "swf-tree 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-tree 0.9.0 (git+https://github.com/open-flash/swf-tree.git?rev=524f19adc098d83900e94bbe693887a63b50786d)", ] [[package]] name = "swf-parser-fuzz" version = "0.0.1" dependencies = [ - "libfuzzer-sys 0.1.0 (git+https://github.com/rust-fuzz/libfuzzer-sys.git)", + "libfuzzer-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "swf-parser 0.9.0", ] [[package]] name = "swf-tree" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/open-flash/swf-tree.git?rev=524f19adc098d83900e94bbe693887a63b50786d#524f19adc098d83900e94bbe693887a63b50786d" dependencies = [ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", @@ -347,7 +347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14" -"checksum libfuzzer-sys 0.1.0 (git+https://github.com/rust-fuzz/libfuzzer-sys.git)" = "" +"checksum libfuzzer-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad9e2afbd25dd3fd52743daa04034276fa7ef9391b28c41d1f5c8ce258fe428b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lzma-rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9550ba35a4d6bb6be7f273bce93af3a3141c517bf7d7298763a7149e1bdb9af5" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" @@ -367,7 +367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum swf-fixed 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3c575807f18641c5e079568b53630a46baf441da57caccd9ced9a9ee70f941" -"checksum swf-tree 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50adec8b597acef58e7405d1aed5f3306f8628f848505925e568d070d2a1d902" +"checksum swf-tree 0.9.0 (git+https://github.com/open-flash/swf-tree.git?rev=524f19adc098d83900e94bbe693887a63b50786d)" = "" "checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" diff --git a/rs/fuzz/Cargo.toml b/rs/fuzz/Cargo.toml index 6c441b1..9bd5522 100644 --- a/rs/fuzz/Cargo.toml +++ b/rs/fuzz/Cargo.toml @@ -3,14 +3,14 @@ name = "swf-parser-fuzz" version = "0.0.1" authors = ["Automatically generated"] publish = false +edition = "2018" [package.metadata] cargo-fuzz = true -[dependencies.swf-parser] -path = ".." -[dependencies.libfuzzer-sys] -git = "https://github.com/rust-fuzz/libfuzzer-sys.git" +[dependencies] +swf-parser = {path = ".."} +libfuzzer-sys = "^0.1.1" # Prevent this from interfering with workspaces [workspace] @@ -19,3 +19,7 @@ members = ["."] [[bin]] name = "swf" path = "fuzz_targets/swf.rs" + +[[bin]] +name = "tag" +path = "fuzz_targets/tag.rs" diff --git a/rs/fuzz/fuzz_targets/swf.rs b/rs/fuzz/fuzz_targets/swf.rs index 8449f54..8c877c4 100644 --- a/rs/fuzz/fuzz_targets/swf.rs +++ b/rs/fuzz/fuzz_targets/swf.rs @@ -1,6 +1,6 @@ #![no_main] -#[macro_use] extern crate libfuzzer_sys; -extern crate swf_parser; + +use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { let _ = swf_parser::complete::parse_swf(data); diff --git a/rs/fuzz/fuzz_targets/tag.rs b/rs/fuzz/fuzz_targets/tag.rs new file mode 100644 index 0000000..211082d --- /dev/null +++ b/rs/fuzz/fuzz_targets/tag.rs @@ -0,0 +1,9 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + if let Some((swf_version, data)) = data.split_first() { + let _ = swf_parser::complete::parse_tag(data, *swf_version); + } +}); diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index cd2299b..66b4dea 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -1396,3 +1396,40 @@ pub fn parse_video_frame(input: &[u8]) -> NomResult<&[u8], ast::tags::VideoFrame }, )) } + +#[cfg(test)] +mod tests { + use std::path::Path; + use super::parse_tag; + use swf_tree::Tag; + use ::test_generator::test_expand_paths; + + test_expand_paths! { test_parse_tag; "../tests/tags/*/*/" } + fn test_parse_tag(path: &str) { + let path: &Path = Path::new(path); + let name = path + .components() + .last() + .unwrap() + .as_os_str() + .to_str() + .expect("Failed to retrieve sample name"); + let input_path = path.join("input.bytes"); + let input_bytes: Vec = ::std::fs::read(input_path).expect("Failed to read input"); + + let swf_version: u8 = match name { + "po2-swf5" => 5, + _ => 10, + }; + + let (remaining_bytes, actual_value) = parse_tag(&input_bytes, swf_version); + + let expected_path = path.join("value.json"); + let expected_file = ::std::fs::File::open(expected_path).expect("Failed to open expected value file"); + let expected_reader = ::std::io::BufReader::new(expected_file); + let expected_value = serde_json_v8::from_reader::<_, Tag>(expected_reader).expect("Failed to read AST"); + + assert_eq!(actual_value, Some(expected_value)); + assert_eq!(remaining_bytes, &[] as &[u8]); + } +} diff --git a/rs/src/lib.rs b/rs/src/lib.rs index c656e7d..82b9738 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -8,15 +8,10 @@ pub use swf_tree; mod tests { use std::io::{Read, Write}; use std::path::Path; - use ::swf_tree::Movie; use nom::IResult as NomResult; - use swf_tree::Tag; - use ::test_generator::test_expand_paths; - use crate::complete::parse_swf; - use crate::complete::parse_tag; test_expand_paths! { test_parse_movie; "../tests/movies/*/" } fn test_parse_movie(path: &str) { @@ -55,35 +50,6 @@ mod tests { assert_eq!(actual_movie, expected_movie); } - test_expand_paths! { test_parse_tag; "../tests/tags/*/*/" } - fn test_parse_tag(path: &str) { - let path: &Path = Path::new(path); - let name = path - .components() - .last() - .unwrap() - .as_os_str() - .to_str() - .expect("Failed to retrieve sample name"); - let input_path = path.join("input.bytes"); - let input_bytes: Vec = ::std::fs::read(input_path).expect("Failed to read input"); - - let swf_version: u8 = match name { - "po2-swf5" => 5, - _ => 10, - }; - - let (remaining_bytes, actual_value) = parse_tag(&input_bytes, swf_version); - - let expected_path = path.join("value.json"); - let expected_file = ::std::fs::File::open(expected_path).expect("Failed to open expected value file"); - let expected_reader = ::std::io::BufReader::new(expected_file); - let expected_value = serde_json_v8::from_reader::<_, Tag>(expected_reader).expect("Failed to read AST"); - - assert_eq!(actual_value, Some(expected_value)); - assert_eq!(remaining_bytes, &[] as &[u8]); - } - macro_rules! test_various_parser_impl { ($name:ident, $glob:expr, $parser:ident, $type:ty) => { test_expand_paths! { $name; $glob } From f700ad0b34684eb08a239ee61a6908e073cae645 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 01:41:31 +0100 Subject: [PATCH 3/9] [rs] Propagate string encoding errors --- CHANGELOG.md | 1 + rs/src/complete/tag.rs | 13 +++++++++++-- rs/src/lib.rs | 8 ++++---- rs/src/streaming/basic_data_types.rs | 17 +++++++++++++---- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e878d33..d9cf1d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - **[Feature]** Add experimental streaming parser. - **[Fix]** Remove `nom` macros. - **[Fix]** Add `clippy` support. +- **[Fix]** Propagate string encoding errors. ## Typescript diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index 66b4dea..acf8c2c 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -1399,10 +1399,10 @@ pub fn parse_video_frame(input: &[u8]) -> NomResult<&[u8], ast::tags::VideoFrame #[cfg(test)] mod tests { - use std::path::Path; use super::parse_tag; - use swf_tree::Tag; use ::test_generator::test_expand_paths; + use std::path::Path; + use swf_tree::Tag; test_expand_paths! { test_parse_tag; "../tests/tags/*/*/" } fn test_parse_tag(path: &str) { @@ -1432,4 +1432,13 @@ mod tests { assert_eq!(actual_value, Some(expected_value)); assert_eq!(remaining_bytes, &[] as &[u8]); } + + // #[test] + // fn test_fuzzing() { + // let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-11af1aace812a45645afba61530f849576e63669"); + // + // let (swf_version, input_bytes) = artifact.split_first().unwrap(); + // + // let _ = parse_tag(input_bytes, *swf_version); + // } } diff --git a/rs/src/lib.rs b/rs/src/lib.rs index 82b9738..e892531 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -6,12 +6,12 @@ pub use swf_tree; #[cfg(test)] mod tests { - use std::io::{Read, Write}; - use std::path::Path; + use crate::complete::parse_swf; use ::swf_tree::Movie; - use nom::IResult as NomResult; use ::test_generator::test_expand_paths; - use crate::complete::parse_swf; + use nom::IResult as NomResult; + use std::io::{Read, Write}; + use std::path::Path; test_expand_paths! { test_parse_movie; "../tests/movies/*/" } fn test_parse_movie(path: &str) { diff --git a/rs/src/streaming/basic_data_types.rs b/rs/src/streaming/basic_data_types.rs index 95023dd..4194abd 100644 --- a/rs/src/streaming/basic_data_types.rs +++ b/rs/src/streaming/basic_data_types.rs @@ -23,12 +23,18 @@ pub fn parse_bool_bits((input_slice, bit_pos): (&[u8], usize)) -> NomResult<(&[u /// Parse a sequence of bytes up to the end of input or first nul-byte. If there /// is a nul-byte, it is consumed but not included in the result. +// TODO: Move to `crate::complete::basic_data_types` +// TODO: Check encoding (assuming UTF-8) pub fn parse_block_c_string(input: &[u8]) -> NomResult<&[u8], String> { - let input = match memchr::memchr(0, input) { + let raw = match memchr::memchr(0, input) { Some(idx) => &input[0..idx], None => input, }; - Ok((&[], String::from_utf8(input.to_vec()).unwrap())) + + match std::str::from_utf8(raw) { + Ok(checked) => Ok((&[], checked.to_string())), + Err(_) => Err(nom::Err::Error((input, nom::error::ErrorKind::Verify))), + } } /// Parse a null-terminated sequence of bytes. The nul-byte is consumed but not included in the @@ -36,10 +42,13 @@ pub fn parse_block_c_string(input: &[u8]) -> NomResult<&[u8], String> { pub fn parse_c_string(input: &[u8]) -> NomResult<&[u8], String> { const NUL_BYTE: &[u8] = b"\x00"; - let (input, str) = nom::bytes::streaming::take_until(NUL_BYTE)(input)?; + let (input, raw) = nom::bytes::streaming::take_until(NUL_BYTE)(input)?; let (input, _) = nom::bytes::streaming::take(NUL_BYTE.len())(input)?; - Ok((input, String::from_utf8(str.to_vec()).unwrap())) + match std::str::from_utf8(raw) { + Ok(checked) => Ok((input, checked.to_string())), + Err(_) => Err(nom::Err::Error((input, nom::error::ErrorKind::Verify))), + } } /// Parse the variable-length encoded little-endian representation of an unsigned 32-bit integer From 2169ac4fc948aa7da2e5b684b728db19289abb3e Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 01:53:18 +0100 Subject: [PATCH 4/9] [rs] Fix panic on invalid image type --- CHANGELOG.md | 1 + rs/src/complete/image.rs | 2 +- rs/src/complete/tag.rs | 20 +++++++++++--------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9cf1d6..0c0b3bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - **[Fix]** Remove `nom` macros. - **[Fix]** Add `clippy` support. - **[Fix]** Propagate string encoding errors. +- **[Fix]** Fix panic on invalid image type. ## Typescript diff --git a/rs/src/complete/image.rs b/rs/src/complete/image.rs index 539b242..78b9318 100644 --- a/rs/src/complete/image.rs +++ b/rs/src/complete/image.rs @@ -115,5 +115,5 @@ pub fn get_gif_image_dimensions(input: &[u8]) -> Result { } pub fn test_image_start(image_data: &[u8], start_bytes: &[u8]) -> bool { - image_data[..start_bytes.len()] == *start_bytes + image_data.len() >= start_bytes.len() && image_data[..start_bytes.len()] == *start_bytes } diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index acf8c2c..f536c55 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -182,7 +182,9 @@ pub fn parse_define_bits(input: &[u8], swf_version: u8) -> NomResult<&[u8], ast: }, )) } else { - panic!("UnknownBitmapType"); + // UnknownBitmapType + // TODO: Better error + Err(nom::Err::Error((input, nom::error::ErrorKind::Verify))) } } @@ -1433,12 +1435,12 @@ mod tests { assert_eq!(remaining_bytes, &[] as &[u8]); } - // #[test] - // fn test_fuzzing() { - // let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-11af1aace812a45645afba61530f849576e63669"); - // - // let (swf_version, input_bytes) = artifact.split_first().unwrap(); - // - // let _ = parse_tag(input_bytes, *swf_version); - // } +// #[test] +// fn test_fuzzing() { +// let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-7ba1a6a7ebb1cf2f1fb3883599dd82bd96e6b271"); +// +// let (swf_version, input_bytes) = artifact.split_first().unwrap(); +// +// let _ = parse_tag(input_bytes, *swf_version); +// } } From 9f253786db2e520441ab1c0393ab3f9b34c70460 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 15:09:06 +0100 Subject: [PATCH 5/9] [rs] Fix panic on incomplete `DefineBitsLossless` --- CHANGELOG.md | 1 + rs/src/complete/base.rs | 12 ++++++++++++ rs/src/complete/mod.rs | 1 + rs/src/complete/tag.rs | 5 +++-- tests/local-tags/raw/incomplete-bitmap/input.bytes | Bin 0 -> 6 bytes .../raw/incomplete-bits-lossless/input.bytes | Bin 0 -> 6 bytes tests/local-tags/raw/non-utf8-string/input.bytes | 1 + 7 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 rs/src/complete/base.rs create mode 100644 tests/local-tags/raw/incomplete-bitmap/input.bytes create mode 100644 tests/local-tags/raw/incomplete-bits-lossless/input.bytes create mode 100644 tests/local-tags/raw/non-utf8-string/input.bytes diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c0b3bd..e0c223e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - **[Fix]** Add `clippy` support. - **[Fix]** Propagate string encoding errors. - **[Fix]** Fix panic on invalid image type. +- **[Fix]** Fix panic on incomplete `DefineBitsLossless`. ## Typescript diff --git a/rs/src/complete/base.rs b/rs/src/complete/base.rs new file mode 100644 index 0000000..f549880 --- /dev/null +++ b/rs/src/complete/base.rs @@ -0,0 +1,12 @@ +use nom::IResult as NomResult; + +/// Creates a parser skipping `count` bytes. +pub(crate) fn skip>(count: C) -> impl Fn(I) -> NomResult + where + I: nom::InputIter + nom::InputTake, + C: nom::ToUsize, +{ + use nom::combinator::map; + use nom::bytes::complete::take; + map(take(count), |_| ()) +} diff --git a/rs/src/complete/mod.rs b/rs/src/complete/mod.rs index 8b4778b..d6a3a06 100644 --- a/rs/src/complete/mod.rs +++ b/rs/src/complete/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod base; pub(crate) mod button; pub(crate) mod display; pub(crate) mod gradient; diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index f536c55..cb51e5c 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -31,6 +31,7 @@ use std::convert::TryFrom; use swf_tree as ast; use swf_tree::text::FontAlignmentZone; use swf_tree::{ButtonCondAction, Glyph}; +use crate::complete::base::skip; /// Parses that tag at the start of `input`. /// @@ -390,7 +391,7 @@ fn parse_define_bits_lossless_any( ) -> NomResult<&[u8], ast::tags::DefineBitmap> { let (input, id) = parse_le_u16(input)?; let data: Vec = input.to_vec(); - let input = &input[1..]; // BitmapFormat + let (input, _) = skip(1usize)(input)?; // BitmapFormat let (input, width) = parse_le_u16(input)?; let (_, height) = parse_le_u16(input)?; let input: &[u8] = &[][..]; @@ -1437,7 +1438,7 @@ mod tests { // #[test] // fn test_fuzzing() { -// let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-7ba1a6a7ebb1cf2f1fb3883599dd82bd96e6b271"); +// let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-90531a762dd2b32cdab0aeb7a0038696e71eecce"); // // let (swf_version, input_bytes) = artifact.split_first().unwrap(); // diff --git a/tests/local-tags/raw/incomplete-bitmap/input.bytes b/tests/local-tags/raw/incomplete-bitmap/input.bytes new file mode 100644 index 0000000000000000000000000000000000000000..f254350d09c4cb836c5cc9cf9379afff08c96a3d GIT binary patch literal 6 NcmZQ*W@KPs2LJ+~0E_?t literal 0 HcmV?d00001 diff --git a/tests/local-tags/raw/incomplete-bits-lossless/input.bytes b/tests/local-tags/raw/incomplete-bits-lossless/input.bytes new file mode 100644 index 0000000000000000000000000000000000000000..3c4d0954be0898cde7474418d7b5b6d0b0556769 GIT binary patch literal 6 NcmZQz;$&bD0000)02}}S literal 0 HcmV?d00001 diff --git a/tests/local-tags/raw/non-utf8-string/input.bytes b/tests/local-tags/raw/non-utf8-string/input.bytes new file mode 100644 index 0000000..d7c363e --- /dev/null +++ b/tests/local-tags/raw/non-utf8-string/input.bytes @@ -0,0 +1 @@ +† \ No newline at end of file From 96943498c233c0f456ce85a6353ed9f74697070e Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 15:11:58 +0100 Subject: [PATCH 6/9] [rs] Replace `take` with `skip` when ignoring result --- rs/src/complete/base.rs | 8 ++++---- rs/src/complete/tag.rs | 29 +++++++++++++---------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/rs/src/complete/base.rs b/rs/src/complete/base.rs index f549880..e52d34f 100644 --- a/rs/src/complete/base.rs +++ b/rs/src/complete/base.rs @@ -2,11 +2,11 @@ use nom::IResult as NomResult; /// Creates a parser skipping `count` bytes. pub(crate) fn skip>(count: C) -> impl Fn(I) -> NomResult - where - I: nom::InputIter + nom::InputTake, - C: nom::ToUsize, +where + I: nom::InputIter + nom::InputTake, + C: nom::ToUsize, { - use nom::combinator::map; use nom::bytes::complete::take; + use nom::combinator::map; map(take(count), |_| ()) } diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index cb51e5c..548207f 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -1,3 +1,4 @@ +use crate::complete::base::skip; use crate::complete::button::{ parse_button2_cond_action_string, parse_button_record_string, parse_button_sound, ButtonVersion, }; @@ -31,7 +32,6 @@ use std::convert::TryFrom; use swf_tree as ast; use swf_tree::text::FontAlignmentZone; use swf_tree::{ButtonCondAction, Glyph}; -use crate::complete::base::skip; /// Parses that tag at the start of `input`. /// @@ -336,12 +336,11 @@ pub fn parse_define_bits_jpeg3(input: &[u8], swf_version: u8) -> NomResult<&[u8] } pub fn parse_define_bits_jpeg4(input: &[u8]) -> NomResult<&[u8], ast::tags::DefineBitmap> { - use nom::bytes::complete::take; use nom::combinator::map; let (djpeg_data, id) = parse_le_u16(input)?; let (input, data_len) = map(parse_le_u32, |dl| usize::try_from(dl).unwrap())(djpeg_data)?; - let (input, _) = take(2usize)(input)?; // Skip deblock + let (input, _) = skip(2usize)(input)?; // Skip deblock let data = &input[..data_len]; let (media_type, dimensions, data) = if test_image_start(data, &JPEG_START) { @@ -1013,8 +1012,7 @@ pub fn parse_enable_debugger(input: &[u8]) -> NomResult<&[u8], ast::tags::Enable } pub fn parse_enable_debugger2(input: &[u8]) -> NomResult<&[u8], ast::tags::EnableDebugger> { - use nom::bytes::complete::take; - let (input, _) = take(2usize)(input)?; + let (input, _) = skip(2usize)(input)?; let (input, password) = parse_c_string(input)?; Ok((input, ast::tags::EnableDebugger { password })) } @@ -1023,7 +1021,7 @@ pub fn parse_enable_telemetry(input: &[u8]) -> NomResult<&[u8], ast::tags::Telem use nom::bytes::complete::take; use nom::combinator::cond; const HASH_SIZE: usize = 32; - let (input, _) = take(2usize)(input)?; + let (input, _) = skip(2usize)(input)?; let (input, password) = cond(input.len() >= HASH_SIZE, take(HASH_SIZE))(input)?; Ok(( input, @@ -1089,12 +1087,11 @@ pub fn parse_import_assets(input: &[u8]) -> NomResult<&[u8], ast::tags::ImportAs #[allow(unused_variables)] pub fn parse_import_assets2(input: &[u8]) -> NomResult<&[u8], ast::tags::ImportAssets> { - use nom::bytes::complete::take; use nom::combinator::map; use nom::multi::count; let (input, url) = parse_c_string(input)?; - let (input, _) = take(2usize)(input)?; + let (input, _) = skip(2usize)(input)?; let (input, asset_count) = map(parse_le_u16, usize::from)(input)?; let (input, assets) = count(parse_named_id, asset_count)(input)?; Ok((input, ast::tags::ImportAssets { url, assets })) @@ -1436,12 +1433,12 @@ mod tests { assert_eq!(remaining_bytes, &[] as &[u8]); } -// #[test] -// fn test_fuzzing() { -// let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-90531a762dd2b32cdab0aeb7a0038696e71eecce"); -// -// let (swf_version, input_bytes) = artifact.split_first().unwrap(); -// -// let _ = parse_tag(input_bytes, *swf_version); -// } + // #[test] + // fn test_fuzzing() { + // let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-90531a762dd2b32cdab0aeb7a0038696e71eecce"); + // + // let (swf_version, input_bytes) = artifact.split_first().unwrap(); + // + // let _ = parse_tag(input_bytes, *swf_version); + // } } From 01b56f500c603c2676bddb03865deed5d0f9a1c2 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 15:24:42 +0100 Subject: [PATCH 7/9] [rs] Fix panic on unknown audio codec code --- CHANGELOG.md | 1 + rs/src/complete/sound.rs | 36 +++++++++--------- rs/src/complete/tag.rs | 21 +++++----- .../raw/unknown-audio-codec/input.bytes | Bin 0 -> 6 bytes 4 files changed, 31 insertions(+), 27 deletions(-) create mode 100644 tests/local-tags/raw/unknown-audio-codec/input.bytes diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c223e..64b3a7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - **[Fix]** Propagate string encoding errors. - **[Fix]** Fix panic on invalid image type. - **[Fix]** Fix panic on incomplete `DefineBitsLossless`. +- **[Fix]** Fix panic on unknown audio codec code. ## Typescript diff --git a/rs/src/complete/sound.rs b/rs/src/complete/sound.rs index 3ca6fab..f759582 100644 --- a/rs/src/complete/sound.rs +++ b/rs/src/complete/sound.rs @@ -2,27 +2,27 @@ use nom::number::complete::{le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u use nom::IResult as NomResult; use swf_tree as ast; -pub fn sound_rate_from_id(sound_rate_id: u8) -> ast::SoundRate { - match sound_rate_id { - 0 => ast::SoundRate::SoundRate5500, - 1 => ast::SoundRate::SoundRate11000, - 2 => ast::SoundRate::SoundRate22000, - 3 => ast::SoundRate::SoundRate44000, - _ => panic!("Unexpected sound rate id"), +pub fn sound_rate_from_code(sound_rate_code: u8) -> Result { + match sound_rate_code { + 0 => Ok(ast::SoundRate::SoundRate5500), + 1 => Ok(ast::SoundRate::SoundRate11000), + 2 => Ok(ast::SoundRate::SoundRate22000), + 3 => Ok(ast::SoundRate::SoundRate44000), + _ => Err(()), } } -pub fn audio_coding_format_from_id(audio_coding_format_id: u8) -> ast::AudioCodingFormat { - match audio_coding_format_id { - 0 => ast::AudioCodingFormat::UncompressedNativeEndian, - 1 => ast::AudioCodingFormat::Adpcm, - 2 => ast::AudioCodingFormat::Mp3, - 3 => ast::AudioCodingFormat::UncompressedLittleEndian, - 4 => ast::AudioCodingFormat::Nellymoser16, - 5 => ast::AudioCodingFormat::Nellymoser8, - 6 => ast::AudioCodingFormat::Nellymoser, - 11 => ast::AudioCodingFormat::Speex, - _ => panic!("Unexpected audio coding format id"), +pub fn audio_coding_format_from_code(audio_codec_code: u8) -> Result { + match audio_codec_code { + 0 => Ok(ast::AudioCodingFormat::UncompressedNativeEndian), + 1 => Ok(ast::AudioCodingFormat::Adpcm), + 2 => Ok(ast::AudioCodingFormat::Mp3), + 3 => Ok(ast::AudioCodingFormat::UncompressedLittleEndian), + 4 => Ok(ast::AudioCodingFormat::Nellymoser16), + 5 => Ok(ast::AudioCodingFormat::Nellymoser8), + 6 => Ok(ast::AudioCodingFormat::Nellymoser), + 11 => Ok(ast::AudioCodingFormat::Speex), + _ => Err(()), } } diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index 548207f..c2f2788 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -11,7 +11,7 @@ use crate::complete::image::{get_jpeg_image_dimensions, test_image_start, ERRONE use crate::complete::morph_shape::{parse_morph_shape, MorphShapeVersion}; use crate::complete::shape::{parse_glyph, parse_shape, ShapeVersion}; use crate::complete::sound::{ - audio_coding_format_from_id, is_uncompressed_audio_coding_format, parse_sound_info, sound_rate_from_id, + audio_coding_format_from_code, is_uncompressed_audio_coding_format, parse_sound_info, sound_rate_from_code, }; use crate::complete::text::{ grid_fitting_from_code, parse_csm_table_hint_bits, parse_font_alignment_zone, parse_font_layout, parse_offset_glyphs, @@ -898,8 +898,10 @@ fn parse_define_sound(input: &[u8]) -> NomResult<&[u8], ast::tags::DefineSound> } else { ast::SoundSize::SoundSize8 }; - let sound_rate = sound_rate_from_id((flags >> 2) & 0b11); - let format = audio_coding_format_from_id((flags >> 4) & 0b1111); + let sound_rate = + sound_rate_from_code((flags >> 2) & 0b11).map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; + let format = audio_coding_format_from_code((flags >> 4) & 0b1111) + .map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; let (input, sample_count) = parse_le_u32(input)?; let (input, data) = parse_bytes(input)?; @@ -1321,7 +1323,8 @@ fn parse_sound_stream_head_any(input: &[u8]) -> NomResult<&[u8], ast::tags::Soun } else { ast::SoundSize::SoundSize8 }; - let playback_sound_rate = sound_rate_from_id(((flags >> 2) & 0b11) as u8); + let playback_sound_rate = sound_rate_from_code(((flags >> 2) & 0b11) as u8) + .map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; // Bits [4, 7] are reserved let stream_sound_type = if (flags & (1 << 8)) != 0 { ast::SoundType::Stereo @@ -1333,8 +1336,10 @@ fn parse_sound_stream_head_any(input: &[u8]) -> NomResult<&[u8], ast::tags::Soun } else { ast::SoundSize::SoundSize8 }; - let stream_sound_rate = sound_rate_from_id(((flags >> 10) & 0b11) as u8); - let stream_format = audio_coding_format_from_id(((flags >> 12) & 0b1111) as u8); + let stream_sound_rate = sound_rate_from_code(((flags >> 10) & 0b11) as u8) + .map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; + let stream_format = audio_coding_format_from_code(((flags >> 12) & 0b1111) as u8) + .map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; let (input, stream_sample_count) = parse_le_u16(input)?; let (input, latency_seek) = cond(stream_format == ast::AudioCodingFormat::Mp3, parse_le_i16)(input)?; Ok(( @@ -1435,10 +1440,8 @@ mod tests { // #[test] // fn test_fuzzing() { - // let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-90531a762dd2b32cdab0aeb7a0038696e71eecce"); - // + // let artifact: &[u8] = include_bytes!("../../../tests/local-tags/raw/unknown-audio-codec/crash-dca088e48244ea9d0dc269a9be48d44502fd825a"); // let (swf_version, input_bytes) = artifact.split_first().unwrap(); - // // let _ = parse_tag(input_bytes, *swf_version); // } } diff --git a/tests/local-tags/raw/unknown-audio-codec/input.bytes b/tests/local-tags/raw/unknown-audio-codec/input.bytes new file mode 100644 index 0000000000000000000000000000000000000000..ba2823ac45b2b0f3b03d9ec9a39b3b87b4d7ea61 GIT binary patch literal 6 NcmZo@W?^U+000KN0h<5- literal 0 HcmV?d00001 From 9d4533aa2e9bf9aec43c627b1580918d03f677f7 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 17:26:42 +0100 Subject: [PATCH 8/9] [rs] Fix panic on invalid CSM text settings --- CHANGELOG.md | 1 + rs/src/complete/tag.rs | 8 ++-- rs/src/complete/text.rs | 39 ++++++++++-------- .../raw/invalid-csm-text-settings/input.bytes | Bin 0 -> 6 bytes .../raw/unknown-grid-fitting/input.bytes | Bin 0 -> 6 bytes 5 files changed, 27 insertions(+), 21 deletions(-) create mode 100644 tests/local-tags/raw/invalid-csm-text-settings/input.bytes create mode 100644 tests/local-tags/raw/unknown-grid-fitting/input.bytes diff --git a/CHANGELOG.md b/CHANGELOG.md index 64b3a7a..290279f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - **[Fix]** Fix panic on invalid image type. - **[Fix]** Fix panic on incomplete `DefineBitsLossless`. - **[Fix]** Fix panic on unknown audio codec code. +- **[Fix]** Fix panic on invalid CSM text settings. ## Typescript diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index c2f2788..0f214ce 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -139,8 +139,10 @@ pub fn parse_csm_text_settings(input: &[u8]) -> NomResult<&[u8], ast::tags::CsmT let (input, text_id) = parse_le_u16(input)?; let (input, flags) = parse_u8(input)?; // Skip bits [0, 2] - let fitting = grid_fitting_from_code((flags >> 3) & 0b111); - let renderer = text_renderer_from_code((flags >> 6) & 0b11); + let fitting = grid_fitting_from_code((flags >> 3) & 0b111) + .map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; + let renderer = text_renderer_from_code((flags >> 6) & 0b11) + .map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; let (input, thickness) = parse_le_f32(input)?; let (input, sharpness) = parse_le_f32(input)?; // TODO: Skip 1 byte / assert 1 byte is available @@ -1440,7 +1442,7 @@ mod tests { // #[test] // fn test_fuzzing() { - // let artifact: &[u8] = include_bytes!("../../../tests/local-tags/raw/unknown-audio-codec/crash-dca088e48244ea9d0dc269a9be48d44502fd825a"); + // let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-e68e5e302143435eac15d06b9fa5c56bc13902d3"); // let (swf_version, input_bytes) = artifact.split_first().unwrap(); // let _ = parse_tag(input_bytes, *swf_version); // } diff --git a/rs/src/complete/text.rs b/rs/src/complete/text.rs index 9d4946b..330da8f 100644 --- a/rs/src/complete/text.rs +++ b/rs/src/complete/text.rs @@ -29,33 +29,36 @@ pub enum FontInfoVersion { FontInfo2, } -pub(crate) fn grid_fitting_from_code(grid_fitting_code: u8) -> ast::text::GridFitting { +pub(crate) fn grid_fitting_from_code(grid_fitting_code: u8) -> Result { match grid_fitting_code { - 0 => ast::text::GridFitting::None, - 1 => ast::text::GridFitting::Pixel, - 2 => ast::text::GridFitting::SubPixel, - _ => panic!("UnexpectedGridFittingCode: {}", grid_fitting_code), + 0 => Ok(ast::text::GridFitting::None), + 1 => Ok(ast::text::GridFitting::Pixel), + 2 => Ok(ast::text::GridFitting::SubPixel), + _ => Err(()), } } pub fn parse_csm_table_hint_bits(input: (&[u8], usize)) -> NomResult<(&[u8], usize), ast::text::CsmTableHint> { - fn csm_table_hint_from_id(csm_table_hint_id: u32) -> ast::text::CsmTableHint { - match csm_table_hint_id { - 0 => ast::text::CsmTableHint::Thin, - 1 => ast::text::CsmTableHint::Medium, - 2 => ast::text::CsmTableHint::Thick, - _ => panic!("UnexpectedCsmTableHintId: {}", csm_table_hint_id), - } - } + let (input, code) = parse_u32_bits(input, 2)?; + let csm_table_hint = + csm_table_hint_from_code(code).map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Switch)))?; + Ok((input, csm_table_hint)) +} - nom::combinator::map(|i| parse_u32_bits(i, 2), csm_table_hint_from_id)(input) +fn csm_table_hint_from_code(code: u32) -> Result { + match code { + 0 => Ok(ast::text::CsmTableHint::Thin), + 1 => Ok(ast::text::CsmTableHint::Medium), + 2 => Ok(ast::text::CsmTableHint::Thick), + _ => Err(()), + } } -pub(crate) fn text_renderer_from_code(text_renderer_code: u8) -> ast::text::TextRenderer { +pub(crate) fn text_renderer_from_code(text_renderer_code: u8) -> Result { match text_renderer_code { - 0 => ast::text::TextRenderer::Normal, - 1 => ast::text::TextRenderer::Advanced, - _ => panic!("UnexpectedTextRendererCode: {}", text_renderer_code), + 0 => Ok(ast::text::TextRenderer::Normal), + 1 => Ok(ast::text::TextRenderer::Advanced), + _ => Err(()), } } diff --git a/tests/local-tags/raw/invalid-csm-text-settings/input.bytes b/tests/local-tags/raw/invalid-csm-text-settings/input.bytes new file mode 100644 index 0000000000000000000000000000000000000000..6483386ae4ca5d0a4a0923a59fe08e5d7f1cb079 GIT binary patch literal 6 NcmWe=7UE#|3IGI30iyr_ literal 0 HcmV?d00001 diff --git a/tests/local-tags/raw/unknown-grid-fitting/input.bytes b/tests/local-tags/raw/unknown-grid-fitting/input.bytes new file mode 100644 index 0000000000000000000000000000000000000000..775bfb6376b1f26b41e7b92f5581d953af7e7c49 GIT binary patch literal 6 NcmWe=7GmJ|4*&#A0j&T4 literal 0 HcmV?d00001 From 311ad7e93100ded90c64b0b8fa5b595dfa43f7c2 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 10 Nov 2019 17:40:11 +0100 Subject: [PATCH 9/9] [rs] Fix remaining unknown bitmap type panics --- rs/src/complete/tag.rs | 11 +++++++---- .../raw/invalid-define-bits-jpeg2-type/input.bytes | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 tests/local-tags/raw/invalid-define-bits-jpeg2-type/input.bytes diff --git a/rs/src/complete/tag.rs b/rs/src/complete/tag.rs index 0f214ce..633c5a9 100644 --- a/rs/src/complete/tag.rs +++ b/rs/src/complete/tag.rs @@ -279,7 +279,8 @@ pub fn parse_define_bits_jpeg2(input: &[u8], swf_version: u8) -> NomResult<&[u8] } else if test_image_start(&data, &GIF_START) { (ast::ImageType::Gif, get_gif_image_dimensions(&data).unwrap()) } else { - panic!("UnknownBitmapType"); + // UnknownBitmapType + return Err(nom::Err::Error((input, nom::error::ErrorKind::Verify))); }; Ok(( @@ -320,7 +321,8 @@ pub fn parse_define_bits_jpeg3(input: &[u8], swf_version: u8) -> NomResult<&[u8] data.to_vec(), ) } else { - panic!("UnknownBitmapType"); + // UnknownBitmapType + return Err(nom::Err::Error((input, nom::error::ErrorKind::Verify))); }; let input: &[u8] = &[][..]; @@ -361,7 +363,8 @@ pub fn parse_define_bits_jpeg4(input: &[u8]) -> NomResult<&[u8], ast::tags::Defi data.to_vec(), ) } else { - panic!("UnknownBitmapType"); + // UnknownBitmapType + return Err(nom::Err::Error((input, nom::error::ErrorKind::Verify))); }; let input: &[u8] = &[][..]; @@ -1442,7 +1445,7 @@ mod tests { // #[test] // fn test_fuzzing() { - // let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-e68e5e302143435eac15d06b9fa5c56bc13902d3"); + // let artifact: &[u8] = include_bytes!("../../fuzz/artifacts/tag/crash-2936f4c99f00563c3e02f65fce6cacf968a0073a"); // let (swf_version, input_bytes) = artifact.split_first().unwrap(); // let _ = parse_tag(input_bytes, *swf_version); // } diff --git a/tests/local-tags/raw/invalid-define-bits-jpeg2-type/input.bytes b/tests/local-tags/raw/invalid-define-bits-jpeg2-type/input.bytes new file mode 100644 index 0000000..51ace4c --- /dev/null +++ b/tests/local-tags/raw/invalid-define-bits-jpeg2-type/input.bytes @@ -0,0 +1 @@ +€C4B \ No newline at end of file