@@ -136,74 +136,101 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
136136#[ macro_export]
137137macro_rules! fuzz_target {
138138 ( |$bytes: ident| $body: block) => {
139- /// Auto-generated function
140- #[ no_mangle]
141- pub extern "C" fn rust_fuzzer_test_input( $bytes: & [ u8 ] ) {
142- // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
143- // formatting of the input to that file. This is only intended for
144- // `cargo fuzz`'s use!
139+ const _: ( ) = {
140+ /// Auto-generated function
141+ #[ no_mangle]
142+ pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
143+ // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
144+ // formatting of the input to that file. This is only intended for
145+ // `cargo fuzz`'s use!
145146
146- // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
147- if let Some ( path) = $crate:: RUST_LIBFUZZER_DEBUG_PATH . get( ) {
148- use std:: io:: Write ;
149- let mut file = std:: fs:: File :: create( path)
150- . expect( "failed to create `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
151- writeln!( & mut file, "{:?}" , $bytes)
152- . expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
153- return ;
147+ // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
148+ if let Some ( path) = $crate:: RUST_LIBFUZZER_DEBUG_PATH . get( ) {
149+ use std:: io:: Write ;
150+ let mut file = std:: fs:: File :: create( path)
151+ . expect( "failed to create `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
152+ writeln!( & mut file, "{:?}" , bytes)
153+ . expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
154+ return ;
155+ }
156+
157+ run( bytes)
154158 }
155159
156- $body
157- }
160+ // Split out the actual fuzzer into a separate function which is
161+ // tagged as never being inlined. This ensures that if the fuzzer
162+ // panics there's at least one stack frame which is named uniquely
163+ // according to this specific fuzzer that this is embedded within.
164+ //
165+ // Systems like oss-fuzz try to deduplicate crashes and without this
166+ // panics in separate fuzzers can accidentally appear the same
167+ // because each fuzzer will have a function called
168+ // `rust_fuzzer_test_input`. By using a normal Rust function here
169+ // it's named something like `the_fuzzer_name::_::run` which should
170+ // ideally help prevent oss-fuzz from deduplicate fuzz bugs across
171+ // distinct targets accidentally.
172+ #[ inline( never) ]
173+ fn run( $bytes: & [ u8 ] ) {
174+ $body
175+ }
176+ } ;
158177 } ;
159178
160179 ( |$data: ident: & [ u8 ] | $body: block) => {
161180 $crate:: fuzz_target!( |$data| $body) ;
162181 } ;
163182
164183 ( |$data: ident: $dty: ty| $body: block) => {
165- /// Auto-generated function
166- #[ no_mangle]
167- pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
168- use $crate:: arbitrary:: { Arbitrary , Unstructured } ;
184+ const _: ( ) = {
185+ /// Auto-generated function
186+ #[ no_mangle]
187+ pub extern "C" fn rust_fuzzer_test_input( bytes: & [ u8 ] ) {
188+ use $crate:: arbitrary:: { Arbitrary , Unstructured } ;
169189
170- // Early exit if we don't have enough bytes for the `Arbitrary`
171- // implementation. This helps the fuzzer avoid exploring all the
172- // different not-enough-input-bytes paths inside the `Arbitrary`
173- // implementation. Additionally, it exits faster, letting the fuzzer
174- // get to longer inputs that actually lead to interesting executions
175- // quicker.
176- if bytes. len( ) < <$dty as Arbitrary >:: size_hint( 0 ) . 0 {
177- return ;
178- }
190+ // Early exit if we don't have enough bytes for the `Arbitrary`
191+ // implementation. This helps the fuzzer avoid exploring all the
192+ // different not-enough-input-bytes paths inside the `Arbitrary`
193+ // implementation. Additionally, it exits faster, letting the fuzzer
194+ // get to longer inputs that actually lead to interesting executions
195+ // quicker.
196+ if bytes. len( ) < <$dty as Arbitrary >:: size_hint( 0 ) . 0 {
197+ return ;
198+ }
179199
180- let mut u = Unstructured :: new( bytes) ;
181- let data = <$dty as Arbitrary >:: arbitrary_take_rest( u) ;
200+ let mut u = Unstructured :: new( bytes) ;
201+ let data = <$dty as Arbitrary >:: arbitrary_take_rest( u) ;
182202
183- // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
184- // formatting of the input to that file. This is only intended for
185- // `cargo fuzz`'s use!
203+ // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
204+ // formatting of the input to that file. This is only intended for
205+ // `cargo fuzz`'s use!
186206
187- // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
188- if let Some ( path) = $crate:: RUST_LIBFUZZER_DEBUG_PATH . get( ) {
189- use std:: io:: Write ;
190- let mut file = std:: fs:: File :: create( path)
191- . expect( "failed to create `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
192- ( match data {
193- Ok ( data) => writeln!( & mut file, "{:#?}" , data) ,
194- Err ( err) => writeln!( & mut file, "Arbitrary Error: {}" , err) ,
195- } )
196- . expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
197- return ;
198- }
207+ // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization.
208+ if let Some ( path) = $crate:: RUST_LIBFUZZER_DEBUG_PATH . get( ) {
209+ use std:: io:: Write ;
210+ let mut file = std:: fs:: File :: create( path)
211+ . expect( "failed to create `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
212+ ( match data {
213+ Ok ( data) => writeln!( & mut file, "{:#?}" , data) ,
214+ Err ( err) => writeln!( & mut file, "Arbitrary Error: {}" , err) ,
215+ } )
216+ . expect( "failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file" ) ;
217+ return ;
218+ }
199219
200- let $ data = match data {
201- Ok ( d) => d,
202- Err ( _) => return ,
203- } ;
220+ let data = match data {
221+ Ok ( d) => d,
222+ Err ( _) => return ,
223+ } ;
204224
205- $body
206- }
225+ run( data)
226+ }
227+
228+ // See above for why this is split to a separate function.
229+ #[ inline( never) ]
230+ fn run( $data: $dty) {
231+ $body
232+ }
233+ } ;
207234 } ;
208235}
209236
0 commit comments