1414// See the License for the specific language governing permissions and
1515// limitations under the License.
1616
17- use std:: path:: PathBuf ;
17+ use std:: { ffi :: OsString , path:: PathBuf } ;
1818
19- use clap:: Parser ;
19+ use clap:: { Parser , ValueEnum } ;
2020
2121/// Parse and validate a pickle protocol version string.
2222///
@@ -32,6 +32,50 @@ fn parse_version(s: &str) -> Result<usize, String> {
3232 }
3333}
3434
35+ fn normalize_mutator_args < I > ( args : I ) -> Vec < OsString >
36+ where
37+ I : IntoIterator < Item = OsString > ,
38+ {
39+ let args: Vec < OsString > = args. into_iter ( ) . collect ( ) ;
40+ let mut normalized = Vec :: with_capacity ( args. len ( ) ) ;
41+ let mut idx = 0 ;
42+
43+ while idx < args. len ( ) {
44+ let arg = & args[ idx] ;
45+ if arg == "--mutators" {
46+ normalized. push ( arg. clone ( ) ) ;
47+ idx += 1 ;
48+
49+ let mut saw_mutator = false ;
50+ while idx < args. len ( ) {
51+ let Some ( value) = args[ idx] . to_str ( ) else {
52+ break ;
53+ } ;
54+ if value == "--" || value. starts_with ( '-' ) {
55+ break ;
56+ }
57+ if crate :: mutators:: MutatorKind :: from_str ( value, true ) . is_err ( ) {
58+ break ;
59+ }
60+
61+ if saw_mutator {
62+ normalized. push ( OsString :: from ( "--mutators" ) ) ;
63+ }
64+ normalized. push ( args[ idx] . clone ( ) ) ;
65+ saw_mutator = true ;
66+ idx += 1 ;
67+ }
68+
69+ continue ;
70+ }
71+
72+ normalized. push ( arg. clone ( ) ) ;
73+ idx += 1 ;
74+ }
75+
76+ normalized
77+ }
78+
3579/// Command-line interface for pickle-fuzzer.
3680///
3781/// Supports two modes:
@@ -68,7 +112,7 @@ pub struct Cli {
68112 #[ arg( short, long, default_value_t = 10_000 , requires = "dir" ) ]
69113 pub samples : usize ,
70114
71- /// seed for random number generator (for reproducible, byte-identical generation)
115+ /// seed for reproducible generation
72116 #[ arg( long) ]
73117 pub seed : Option < u64 > ,
74118
@@ -80,8 +124,12 @@ pub struct Cli {
80124 #[ arg( long, default_value_t = 300 ) ]
81125 pub max_opcodes : usize ,
82126
83- /// enable specific mutators (can be specified multiple times)
84- #[ arg( long = "mutators" , value_name = "MUTATOR" , num_args = 1 ..) ]
127+ /// enable specific mutators (repeat the flag or list mutators after one occurrence)
128+ #[ arg(
129+ long = "mutators" ,
130+ value_name = "MUTATOR" ,
131+ action = clap:: ArgAction :: Append
132+ ) ]
85133 pub mutators : Vec < crate :: mutators:: MutatorKind > ,
86134
87135 /// mutation rate (0.0-1.0, probability of applying mutation)
@@ -99,9 +147,17 @@ pub struct Cli {
99147 /// allow NEXT_BUFFER/READONLY_BUFFER opcodes (requires out-of-band buffer support in unpickler)
100148 #[ arg( long) ]
101149 pub allow_buffer : bool ,
150+
151+ /// allow PERSID/BINPERSID opcodes (requires persistent_load support in unpickler)
152+ #[ arg( long) ]
153+ pub allow_persistent_ids : bool ,
102154}
103155
104156impl Cli {
157+ pub fn parse_args ( ) -> Self {
158+ Self :: parse_from ( normalize_mutator_args ( std:: env:: args_os ( ) ) )
159+ }
160+
105161 /// Check if running in batch mode (generating multiple files).
106162 pub fn is_batch_mode ( & self ) -> bool {
107163 self . dir . is_some ( )
@@ -154,6 +210,7 @@ mod tests {
154210 unsafe_mutations : false ,
155211 allow_ext : false ,
156212 allow_buffer : false ,
213+ allow_persistent_ids : false ,
157214 } ;
158215
159216 assert ! ( cli_single. is_single_file_mode( ) ) ;
@@ -172,9 +229,33 @@ mod tests {
172229 unsafe_mutations : false ,
173230 allow_ext : false ,
174231 allow_buffer : false ,
232+ allow_persistent_ids : false ,
175233 } ;
176234
177235 assert ! ( !cli_batch. is_single_file_mode( ) ) ;
178236 assert ! ( cli_batch. is_batch_mode( ) ) ;
179237 }
238+
239+ #[ test]
240+ fn test_normalize_mutator_args_keeps_output_path_positional ( ) {
241+ let normalized = normalize_mutator_args ( [
242+ OsString :: from ( "pickle-fuzzer" ) ,
243+ OsString :: from ( "--mutators" ) ,
244+ OsString :: from ( "bitflip" ) ,
245+ OsString :: from ( "boundary" ) ,
246+ OsString :: from ( "output.pkl" ) ,
247+ ] ) ;
248+
249+ assert_eq ! (
250+ normalized,
251+ vec![
252+ OsString :: from( "pickle-fuzzer" ) ,
253+ OsString :: from( "--mutators" ) ,
254+ OsString :: from( "bitflip" ) ,
255+ OsString :: from( "--mutators" ) ,
256+ OsString :: from( "boundary" ) ,
257+ OsString :: from( "output.pkl" ) ,
258+ ]
259+ ) ;
260+ }
180261}
0 commit comments