Created
January 13, 2023 01:41
-
-
Save CosmicHorrorDev/448e5bf0f1124d5fa7b1291103f62a11 to your computer and use it in GitHub Desktop.
Console windows ansi parsing fuzz target
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![no_main] | |
use blah::{new, old}; | |
use libfuzzer_sys::fuzz_target; | |
fuzz_target!(|data: &[u8]| { | |
if let Ok(s) = std::str::from_utf8(data) { | |
assert_eq!(new::output(s), old::output(s)); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[derive(Clone, Copy, Debug, PartialEq)] | |
pub enum Output { | |
Color(Intense, Color, FgBg), | |
NotAttr, | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
pub enum Intense { | |
Yes, | |
No, | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
pub enum FgBg { | |
Foreground, | |
Background, | |
} | |
impl FgBg { | |
pub fn new(byte: u8) -> Option<Self> { | |
match byte { | |
b'3' => Some(Self::Foreground), | |
b'4' => Some(Self::Background), | |
_ => None, | |
} | |
} | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
pub enum Color { | |
Black, | |
Red, | |
Green, | |
Yellow, | |
Blue, | |
Magenta, | |
Cyan, | |
White, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub mod common; | |
pub mod new; | |
pub mod old; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::str::Bytes; | |
use crate::common::{Color, FgBg, Intense, Output}; | |
pub fn output(part: &str) -> Option<Output> { | |
if let Some((intense, color, fg_bg)) = driver(parse_color, part) { | |
Some(Output::Color(intense, color, fg_bg)) | |
} else if driver(parse_attr, part).is_none() { | |
Some(Output::NotAttr) | |
} else { | |
None | |
} | |
} | |
fn driver<Out>(parse: fn(Bytes<'_>) -> Option<Out>, part: &str) -> Option<Out> { | |
let mut bytes = part.bytes(); | |
loop { | |
while bytes.next()? != b'\x1b' {} | |
if let ret @ Some(_) = (parse)(bytes.clone()) { | |
return ret; | |
} | |
} | |
} | |
// Parses the equivalent of the regex | |
// \x1b\[(3|4)8;5;(8|9|1[0-5])m | |
// for intense or | |
// \x1b\[(3|4)([0-7])m | |
// for normal | |
fn parse_color(mut bytes: Bytes<'_>) -> Option<(Intense, Color, FgBg)> { | |
parse_prefix(&mut bytes)?; | |
let fg_bg = FgBg::new(bytes.next()?)?; | |
let (intense, color) = match bytes.next()? { | |
b @ b'0'..=b'7' => (Intense::No, normal_color_ansi_from_byte(b)?), | |
b'8' => { | |
if &[bytes.next()?, bytes.next()?, bytes.next()?] != b";5;" { | |
return None; | |
} | |
(Intense::Yes, parse_intense_color_ansi(&mut bytes)?) | |
} | |
_ => return None, | |
}; | |
parse_suffix(&mut bytes)?; | |
Some((intense, color, fg_bg)) | |
} | |
// Parses the equivalent of the regex | |
// \x1b\[([1-8])m | |
fn parse_attr(mut bytes: Bytes<'_>) -> Option<u8> { | |
parse_prefix(&mut bytes)?; | |
let attr = match bytes.next()? { | |
attr @ b'1'..=b'8' => attr, | |
_ => return None, | |
}; | |
parse_suffix(&mut bytes)?; | |
Some(attr) | |
} | |
fn parse_prefix(bytes: &mut Bytes<'_>) -> Option<()> { | |
if bytes.next()? == b'[' { | |
Some(()) | |
} else { | |
None | |
} | |
} | |
fn parse_intense_color_ansi(bytes: &mut Bytes<'_>) -> Option<Color> { | |
let color = match bytes.next()? { | |
b'8' => Color::Black, | |
b'9' => Color::Red, | |
b'1' => match bytes.next()? { | |
b'0' => Color::Green, | |
b'1' => Color::Yellow, | |
b'2' => Color::Blue, | |
b'3' => Color::Magenta, | |
b'4' => Color::Cyan, | |
b'5' => Color::White, | |
_ => return None, | |
}, | |
_ => return None, | |
}; | |
Some(color) | |
} | |
fn normal_color_ansi_from_byte(b: u8) -> Option<Color> { | |
let color = match b { | |
b'0' => Color::Black, | |
b'1' => Color::Red, | |
b'2' => Color::Green, | |
b'3' => Color::Yellow, | |
b'4' => Color::Blue, | |
b'5' => Color::Magenta, | |
b'6' => Color::Cyan, | |
b'7' => Color::White, | |
_ => return None, | |
}; | |
Some(color) | |
} | |
fn parse_suffix(bytes: &mut Bytes<'_>) -> Option<()> { | |
if bytes.next()? == b'm' { | |
Some(()) | |
} else { | |
None | |
} | |
} | |
#[test] | |
fn fuzz_crash() { | |
let input = std::str::from_utf8(&[121, 27, 91, 50, 45, 0, 0, 16, 27, 91, 50, 109, 10, 121, 10]) | |
.unwrap(); | |
let output = output(input); | |
assert!(output.is_none(), "{output:?}"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::common::{Color, FgBg, Intense, Output}; | |
use regex::Regex; | |
lazy_static::lazy_static! { | |
static ref INTENSE_COLOR_RE: Regex = Regex::new(r"\x1b\[(3|4)8;5;(8|9|1[0-5])m").unwrap(); | |
static ref NORMAL_COLOR_RE: Regex = Regex::new(r"\x1b\[(3|4)([0-7])m").unwrap(); | |
static ref ATTR_RE: Regex = Regex::new(r"\x1b\[([1-8])m").unwrap(); | |
} | |
pub fn output(part: &str) -> Option<Output> { | |
if let Some(cap) = INTENSE_COLOR_RE.captures(part) { | |
let color = get_color_from_ansi(cap.get(2).unwrap().as_str()); | |
let fg_bg = match cap.get(1).unwrap().as_str() { | |
"3" => FgBg::Foreground, | |
"4" => FgBg::Background, | |
_ => unreachable!(), | |
}; | |
Some(Output::Color(Intense::Yes, color, fg_bg)) | |
} else if let Some(cap) = NORMAL_COLOR_RE.captures(part) { | |
let color = get_color_from_ansi(cap.get(2).unwrap().as_str()); | |
let fg_bg = match cap.get(1).unwrap().as_str() { | |
"3" => FgBg::Foreground, | |
"4" => FgBg::Background, | |
_ => unreachable!(), | |
}; | |
Some(Output::Color(Intense::No, color, fg_bg)) | |
} else if !ATTR_RE.is_match(part) { | |
Some(Output::NotAttr) | |
} else { | |
None | |
} | |
} | |
fn get_color_from_ansi(ansi: &str) -> Color { | |
match ansi { | |
"0" | "8" => Color::Black, | |
"1" | "9" => Color::Red, | |
"2" | "10" => Color::Green, | |
"3" | "11" => Color::Yellow, | |
"4" | "12" => Color::Blue, | |
"5" | "13" => Color::Magenta, | |
"6" | "14" => Color::Cyan, | |
"7" | "15" => Color::White, | |
_ => unreachable!(), | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment