syn
syn copied to clipboard
Found slow units on syn::parse_str using cargofuzz
Some slow parsing behaviors observed during fuzz testing. Not a crash or a critical bug, but addressing these cases can help improve the robustness and efficiency of the parsing logic.
Test on version: v2.0.100
Harness
#![no_main]
extern crate libfuzzer_sys;
extern crate syn;
use libfuzzer_sys::fuzz_target;
use syn::spanned::Spanned;
use syn::parse_str;
fuzz_target!(|data: &str| {
if let Ok(value) = parse_str::<syn::Expr>(data) {
let _ = value.span();
}
});
Problematic input 1
tb|kP--&t*..4|kP--&t&t*..4|kP--&t*..4|q-&t*..4P
|*..4|q-&t*..4P
|kq-&tb|kP--&t*..4|kP--&t&t*..4|kP--&t*..4|q-&t*..4P
|*..4|q-&t*..4P
|kq-&t*..t*..4|q-&t*..4P
|*..4|q-&t*..4P
|kq-&tb|kP--&t*..4|kP--&t&t*..4|kP--&t*..4|q-&t*..4P
|*..4|kP--&t*..4|q-&t*..4P
|*..4|q-&t*..4P
|kq-&t*..t*..4|qkHPk=1%0|s
Execution Output:
Executed fuzz/artifacts_cluster/syn_span2_fuzz/slow-unit-1da6724f047297669763f0d258c9b27c6b92c037 in 162148 ms
Problematic input 2
tb|kP--&t*..4|kP--&t&t*..4|kP--&t*..4|q-&t*..4|kP--&t&t*..4|kP--&t*..4|q-&t*..4P
|*..4|q-&t*..4P
|kq-&tb|kP--&t*..4|kP--&t&t*..4|kP--&t*..4|q-&t*..4P
|*..4|q-&t*..4P
|kqt*..4P
|*..4|q-&t*..4P
|kq-&tb|kP--&t*..4|kP--&t&t*..4|kP--&t*..4|q-&t*..4P
|*..4|q-&t*..4P
|kq-&t*..-&tkHPk=1%0|t*..-&tkHPk=1%0|s
Execution output:
Executed fuzz/artifacts_cluster/syn_span2_fuzz/slow-unit-e02f19b7534f6d394c94700f5038d5f0f3cfa945 in 30809 ms
Environment:
- OS: macOS (aarch64-apple-darwin)
- Rust Version: rustc 1.83.0-nightly
The pattern repetition and inclusion of non-printable characters likely cause the syn parser to repeatedly attempt matching. The combination of nested patterns and malformed syntax may be triggering deep recursion or inefficient parsing paths.
possibly originated from this recursion: src/macros.rs
generate_to_tokens!(
($($arms)* $(#[cfg $cfg_attr])* $name::$variant(_e) => _e.to_tokens($tokens),)
Thanks. Formatted and without libfuzzer-sys dependency:
// [dependencies]
// syn = { version = "2", default-features = false, features = ["full", "parsing", "printing"] }
// quote = { version = "1", default-features = false }
const INPUT: &str = r"
0 | 0 - -&0 * ..0 | 0 - -&0 & 0 * ..0 | 0 - -&0 * ..0 | 0 - &0 * ..0
| *..0 | 0 - &0 * ..0
| 0 - &0 | 0 - -&0 * ..0 | 0 - -&0 & 0 * ..0 | 0 - -&0 * ..0 | 0 - &0 * ..0
| *..0 | 0 - &0 * ..0
| 0 - &0 * ..0 * ..0 | 0 - &0 * ..0
| *..0 | 0 - &0 * ..0
| 0 - &0 | 0 - -&0 * ..0 | 0 - -&0 & 0 * ..0 | 0 - -&0 * ..0 | 0 - &0 * ..0
| *..0 | 0 - -&0 * ..0 | 0 - &0 * ..0
| *..0 | 0 - &0 * ..0
| 0 - &0 * ..0 * ..0 | 0 = 0 % 0 | 0
";
fn main() {
let expr = syn::parse_str::<syn::Expr>(INPUT).unwrap();
let tokens = quote::ToTokens::to_token_stream(&expr);
println!("{}", tokens);
}
Minimized further:
const INPUT: &str = r"
0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0
| ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 | ..0 = 0
";