|
| 1 | +use bson::{doc, Bson, Decimal128}; |
| 2 | +use std::{ |
| 3 | + fs, |
| 4 | + io::{Error, ErrorKind}, |
| 5 | + path::Path, |
| 6 | + str::FromStr, |
| 7 | +}; |
| 8 | + |
| 9 | +fn main() -> std::io::Result<()> { |
| 10 | + let corpus_dir = Path::new("fuzz/corpus"); |
| 11 | + fs::create_dir_all(corpus_dir)?; |
| 12 | + |
| 13 | + // Generate edge cases for each fuzz target |
| 14 | + generate_length_edge_cases(corpus_dir)?; |
| 15 | + generate_type_marker_cases(corpus_dir)?; |
| 16 | + generate_string_edge_cases(corpus_dir)?; |
| 17 | + generate_serialization_cases(corpus_dir)?; |
| 18 | + Ok(()) |
| 19 | +} |
| 20 | + |
| 21 | +fn generate_length_edge_cases(dir: &Path) -> std::io::Result<()> { |
| 22 | + let target_dir = dir.join("malformed_length"); |
| 23 | + fs::create_dir_all(&target_dir)?; |
| 24 | + |
| 25 | + // Invalid length |
| 26 | + fs::write(target_dir.join("invalid_len"), vec![4, 5])?; |
| 27 | + |
| 28 | + // Minimal valid document |
| 29 | + let min_doc = doc! {}; |
| 30 | + fs::write( |
| 31 | + target_dir.join("min_doc"), |
| 32 | + bson::to_vec(&min_doc).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, |
| 33 | + )?; |
| 34 | + |
| 35 | + // Document with length near i32::MAX |
| 36 | + let large_doc = doc! { "a": "b".repeat(i32::MAX as usize / 2) }; |
| 37 | + fs::write( |
| 38 | + target_dir.join("large_doc"), |
| 39 | + bson::to_vec(&large_doc).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, |
| 40 | + )?; |
| 41 | + |
| 42 | + Ok(()) |
| 43 | +} |
| 44 | + |
| 45 | +fn generate_type_marker_cases(dir: &Path) -> std::io::Result<()> { |
| 46 | + let target_dir = dir.join("type_markers"); |
| 47 | + fs::create_dir_all(&target_dir)?; |
| 48 | + |
| 49 | + // Document with all BSON types |
| 50 | + let all_types = doc! { |
| 51 | + "double": 1.0f64, |
| 52 | + "double_nan": f64::NAN, |
| 53 | + "double_infinity": f64::INFINITY, |
| 54 | + "double_neg_infinity": f64::NEG_INFINITY, |
| 55 | + "string": "test", |
| 56 | + "document": doc! {}, |
| 57 | + "array": vec![1, 2, 3], |
| 58 | + "binary": Bson::Binary(bson::Binary { subtype: bson::spec::BinarySubtype::Generic, bytes: vec![1, 2, 3] }), |
| 59 | + "object_id": bson::oid::ObjectId::new(), |
| 60 | + "bool": true, |
| 61 | + "date": bson::DateTime::now(), |
| 62 | + "null": Bson::Null, |
| 63 | + "regex": Bson::RegularExpression(bson::Regex { pattern: "pattern".into(), options: "i".into() }), |
| 64 | + "int32": 123i32, |
| 65 | + "timestamp": bson::Timestamp { time: 12345, increment: 1 }, |
| 66 | + "int64": 123i64, |
| 67 | + "decimal128_nan": Decimal128::from_str("NaN").unwrap(), |
| 68 | + "decimal128_infinity": Decimal128::from_str("Infinity").unwrap(), |
| 69 | + "decimal128_neg_infinity": Decimal128::from_str("-Infinity").unwrap(), |
| 70 | + "min_key": Bson::MinKey, |
| 71 | + "max_key": Bson::MaxKey, |
| 72 | + "undefined": Bson::Undefined |
| 73 | + }; |
| 74 | + fs::write( |
| 75 | + target_dir.join("all_types"), |
| 76 | + bson::to_vec(&all_types).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, |
| 77 | + )?; |
| 78 | + |
| 79 | + Ok(()) |
| 80 | +} |
| 81 | + |
| 82 | +fn generate_string_edge_cases(dir: &Path) -> std::io::Result<()> { |
| 83 | + let target_dir = dir.join("string_handling"); |
| 84 | + fs::create_dir_all(&target_dir)?; |
| 85 | + |
| 86 | + // UTF-8 edge cases |
| 87 | + let utf8_cases = doc! { |
| 88 | + "empty": "", |
| 89 | + "null_bytes": "hello\0world", |
| 90 | + "unicode": "🦀💻🔒", |
| 91 | + "high_surrogate": "\u{10000}", |
| 92 | + "invalid_continuation": Bson::Binary(bson::Binary { |
| 93 | + subtype: bson::spec::BinarySubtype::Generic, |
| 94 | + bytes: vec![0x80u8, 0x80u8, 0x80u8] |
| 95 | + }), |
| 96 | + "overlong": Bson::Binary(bson::Binary { |
| 97 | + subtype: bson::spec::BinarySubtype::Generic, |
| 98 | + bytes: vec![0xC0u8, 0x80u8] |
| 99 | + }) |
| 100 | + }; |
| 101 | + fs::write( |
| 102 | + target_dir.join("utf8_cases"), |
| 103 | + bson::to_vec(&utf8_cases).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, |
| 104 | + )?; |
| 105 | + |
| 106 | + Ok(()) |
| 107 | +} |
| 108 | + |
| 109 | +fn generate_serialization_cases(dir: &Path) -> std::io::Result<()> { |
| 110 | + let target_dir = dir.join("serialization"); |
| 111 | + fs::create_dir_all(&target_dir)?; |
| 112 | + |
| 113 | + // Deeply nested document |
| 114 | + let mut nested_doc = doc! {}; |
| 115 | + let mut current = &mut nested_doc; |
| 116 | + for i in 0..100 { |
| 117 | + let next_doc = doc! {}; |
| 118 | + current.insert(i.to_string(), next_doc); |
| 119 | + current = current |
| 120 | + .get_mut(&i.to_string()) |
| 121 | + .unwrap() |
| 122 | + .as_document_mut() |
| 123 | + .unwrap(); |
| 124 | + } |
| 125 | + fs::write( |
| 126 | + target_dir.join("nested_doc"), |
| 127 | + bson::to_vec(&nested_doc).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, |
| 128 | + )?; |
| 129 | + |
| 130 | + // Document with large binary data |
| 131 | + let large_binary = doc! { |
| 132 | + "binary": Bson::Binary(bson::Binary { |
| 133 | + subtype: bson::spec::BinarySubtype::Generic, |
| 134 | + bytes: vec![0xFF; 1024 * 1024] // 1MB of data |
| 135 | + }) |
| 136 | + }; |
| 137 | + fs::write( |
| 138 | + target_dir.join("large_binary"), |
| 139 | + bson::to_vec(&large_binary).map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, |
| 140 | + )?; |
| 141 | + |
| 142 | + Ok(()) |
| 143 | +} |
0 commit comments