This is an archive of the discontinued LLVM Phabricator instance.

[libFuzzer] Handle unstable edges by disregarding unstable edges
ClosedPublic

Authored by kevinwkt on Jul 23 2018, 11:22 AM.

Details

Summary

Added a new mode within flag -handle_unstable for new unstable handling algorithm that does the following:

When an edge is shown as unstable, copy to UnstableCounters the value 0.
During ApplyUnstableCounters we copy back the value 0 to ModuleInline8bitCounters if the edge was unstable.

This way we would be ignoring completely features that were collected through non-determinism.
Unstable hits would be counted as if it never hit.

Diff Detail

Event Timeline

kevinwkt created this revision.Jul 23 2018, 11:22 AM
kevinwkt updated this revision to Diff 156854.Jul 23 2018, 12:00 PM

Added tests.
Unstable should not have as features all unstable edges such as ini functions nor t functions.
In normal we only check for t function because regex catches det as t functions because of the "t()" in "de-t()".

Dor1s edited the summary of this revision. (Show Details)Jul 23 2018, 1:42 PM
Dor1s added a comment.Jul 23 2018, 1:51 PM

I'm not sure I fully understand that approach. We put 0 into the counters for edges we think are unstable. That's fair, but every time we generate a new input that triggers such edge, we would re-run it twice just to learn again that it was unstable. Why not put something like 0xff to mark it like a covered edge, so that we would ignore any new testcases covering that?

Another concern I have is the following. Let's say we have a testcase that covers edge X in a deterministic way, we save it. Later on, we find another testcases that covers that edge, but demonstrates instability, so we mark the edge as unstable. That sort of thing may happen again and again, an edge might seem to be stable or unstable with different testcases, And we still be blowing the corpus.

What do I misunderstand?

test/fuzzer/handle_unstable_zerounstable.test
10 ↗(On Diff #156854)

Is there anything stable we can check for? If there isn't, I think it would make sense to introduce some deterministic function.

kevinwkt added a comment.EditedJul 23 2018, 1:59 PM

I'm not sure I fully understand that approach. We put 0 into the counters for edges we think are unstable. That's fair, but every time we generate a new input that triggers such edge, we would re-run it twice just to learn again that it was unstable. Why not put something like 0xff to mark it like a covered edge, so that we would ignore any new testcases covering that?

Another concern I have is the following. Let's say we have a testcase that covers edge X in a deterministic way, we save it. Later on, we find another testcases that covers that edge, but demonstrates instability, so we mark the edge as unstable. That sort of thing may happen again and again, an edge might seem to be stable or unstable with different testcases, And we still be blowing the corpus.

What do I misunderstand?

So the reason I think putting it as an uncovered edge is a better solution is that, in the future, if we get an input that hits the the previously unstable edges deterministically, we want to award the deterministically reached input for finding the "new" edge. It may be slower as you commented, but I think that it makes more sense. If we mark those undeterministically reached edges as covered edge, inputs that reach it deterministically in the future (which I believe are the truly interesting inputs) will not be added to corpus.

kevinwkt updated this revision to Diff 156877.Jul 23 2018, 2:04 PM

ptal

Added deterministic checks for tests.

Dor1s added a comment.Jul 23 2018, 3:10 PM

I'm not sure I fully understand that approach. We put 0 into the counters for edges we think are unstable. That's fair, but every time we generate a new input that triggers such edge, we would re-run it twice just to learn again that it was unstable. Why not put something like 0xff to mark it like a covered edge, so that we would ignore any new testcases covering that?

Another concern I have is the following. Let's say we have a testcase that covers edge X in a deterministic way, we save it. Later on, we find another testcases that covers that edge, but demonstrates instability, so we mark the edge as unstable. That sort of thing may happen again and again, an edge might seem to be stable or unstable with different testcases, And we still be blowing the corpus.

What do I misunderstand?

So the reason I think putting it as an uncovered edge is a better solution is that, in the future, if we get an input that hits the the previously unstable edges deterministically, we want to award the deterministically reached input for finding the "new" edge. It may be slower as you commented, but I think that it makes more sense. If we mark those undeterministically reached edges as covered edge, inputs that reach it deterministically in the future (which I believe are the truly interesting inputs) will not be added to corpus.

Thanks for the clarification, I see. So, this approach is a less aggressive compared to https://reviews.llvm.org/D49578

Left two more minor comments. Looks good otherwise.

lib/fuzzer/FuzzerTracePC.cpp
90

nit: if -> else if

test/fuzzer/handle_unstable_zerounstable.test
12 ↗(On Diff #156877)

Any reason not to check for those in` NORMAL` run as well?

Dor1s added a comment.Jul 23 2018, 3:12 PM

Feel free to ask Matt / Kostya to review this once my comments are addressed.

kevinwkt updated this revision to Diff 156898.EditedJul 23 2018, 3:23 PM
kevinwkt marked 3 inline comments as done.

PTAL
Fixed if to else if.

Added det() checks to Normal Runs.

metzman accepted this revision.Jul 23 2018, 5:32 PM

LGTM

This revision is now accepted and ready to land.Jul 23 2018, 5:32 PM
morehouse added inline comments.Jul 24 2018, 9:05 AM
lib/fuzzer/FuzzerTracePC.cpp
93

We can simplify this to

else if (UnstableMode == MinUnstable)
  UnstableCounters[UnstableIdx].Counter = std::min(...)
test/fuzzer/handle_unstable_zerounstable.test
10 ↗(On Diff #156898)

Is there some probability that these functions are marked stable? For example, could all 3 runs hit t0?

30 ↗(On Diff #156898)

I think there's some redundancy here. Let's combine this file with handle_unstable_minunstable.test to a single handle-unstable.test.

kevinwkt updated this revision to Diff 157066.EditedJul 24 2018, 9:57 AM
kevinwkt marked 3 inline comments as done.

ptal
Removed Non-deterministic tests and changed UpdateCounters to use std::min

kevinwkt added inline comments.Jul 24 2018, 10:10 AM
test/fuzzer/handle_unstable_zerounstable.test
10 ↗(On Diff #156898)

Yes, I will only include the ini functions now so that the test is deterministic.

morehouse accepted this revision.Jul 24 2018, 10:19 AM
morehouse added inline comments.
lib/fuzzer/FuzzerTracePC.cpp
93

Can save 1 line by removing braces from this else if.

metzman added inline comments.Jul 24 2018, 10:22 AM
lib/fuzzer/FuzzerTracePC.cpp
93

I generally ask for this because I think multiline if/else bodies that don't have brackets are scary.
Do you still think this change should be made?

If not, then Kevin can you put the ifbody in brackets for consistency.

morehouse added inline comments.Jul 24 2018, 10:28 AM
lib/fuzzer/FuzzerTracePC.cpp
93

I think it's a small detail and don't feel too strongly either way.

On one hand, compact and simple code is easier to read and maintain. On the other hand, a lazy programmer could add another line to the if and introduce a bug.

There is code in libFuzzer with both conventions.

morehouse added inline comments.Jul 24 2018, 10:30 AM
lib/fuzzer/FuzzerTracePC.cpp
93

I think @kcc would generally prefer the simpler and more compact version, but we're splitting hairs here.

kevinwkt updated this revision to Diff 157120.Jul 24 2018, 1:44 PM
kevinwkt marked 5 inline comments as done.

PTAL
Got rid of the extra brackets to make it more compact.

Dor1s updated this revision to Diff 157124.Jul 24 2018, 2:02 PM

Rebase and getting ready to commit.

Herald added subscribers: Restricted Project, llvm-commits, delcypher. · View Herald TranscriptJul 24 2018, 2:02 PM
This revision was automatically updated to reflect the committed changes.

Hi Kyungtak,

The test added in this commit appears to be failing for aarch64 since
it was introduced:

http://lab.llvm.org:8011/builders/clang-cmake-aarch64-full/builds/5515
is failing and covers the range which added this test
http://lab.llvm.org:8011/waterfall?show=clang-cmake-aarch64-full shows
that the test has been failing since that build onwards

Best regards,

Thomas

kevinwkt updated this revision to Diff 157302.EditedJul 25 2018, 10:18 AM

@Dor1s @thopre

This update should fix both tests and should work now.

test/fuzzer/handle-unstable.test