rust-lang-nursery / lazy-static.rs

A small macro for defining lazy evaluated static variables in Rust.
Apache License 2.0
1.9k stars 111 forks source link

Add function `reset` #164

Closed smoelius closed 4 years ago

smoelius commented 4 years ago

This PR adds a function reset that puts all lazy_static variables back in their original, uninitialized states. Such a function facilitates fuzzing with AFL in persistent mode.

In persistent mode, AFL does not launch a new process for each fuzzing run. Rather, the process loops, repeatedly executing the function under test with new fuzzing inputs. This can cause problems for programs that use lazy_static variables in at least two ways:

  1. AFL can give incorrectly low stability reports. Roughly speaking, stability is the fraction of instructions that are executed the same number of times across different runs of the program with the same input. Because a lazy_static variable's initializer is executed only on the variable's first use, the initializer's instructions are counted against stability, causing it to be low.

  2. AFL can fail to report timeouts. Suppose that a fuzzing input X causes a program to use a large number of lazy_static variables, each of which has an expensive initializer. If each of those variables was used for an earlier fuzzing input, then those variables' intializers will not be run on input X, causing the program to take less time than it otherwise would on input X.

Returning each lazy_static variable to its uninitialized state at the end of each fuzzing iteration addresses these problems. This causes each lazy_static variable's initializer to be executed for each fuzzing input that uses that variable. Thus, the initializer's instructions are not counted against stability. On the other hand, the time taken to execute the initializer is counted against each input that uses that variable.

Conceptually, this PR does two things:

The function reset walks the linked list in order to set the initialized lazy_static variables back to their uninitialized states, assigning ONCE_INIT to each Cell<Once> in the process.

A program demonstrating the problems described above can be found here. The program further demonstrates that adding a call to reset at the end of each fuzzing iteration addresses these problems.

dtolnay commented 4 years ago

I would prefer not to do this. It looks like this introduces quite a bit of new complexity in Lazy::get.

Can you suggest any other ways to get better fuzzing in programs with a lazy_static?

smoelius commented 4 years ago

Can you suggest any other ways to get better fuzzing in programs with a lazy_static?

You can abandon AFL persistent mode, of course. But then you give up on the advantages that it provides (e.g., avoiding fork).

Another alternative may be to maintain a "resettable-lazy-static" fork and keep it in sync with the main repository, so that a person can patch-it-in when fuzzing. lazy-static appears to be pretty stable, so this may be a workable solution.

I will take the issue up with the afl.rs maintainers and see what they say.

I understand your position. Thanks a lot for your time.