-
Notifications
You must be signed in to change notification settings - Fork 513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add sanitizer coverage feedback evolution support part2 #47
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Clang sanitizer coverage (sancov) data parsing functions. Supported methods: * raw unified data (preferred method) * individual data per executable/DSO (not preferred since lots of data lost if instrumented code exits abnormally or with sanitizer unhandled signal (common in Android OS) For raw-unpack method a global (shared across workers) Trie is created for the chosen initial seed and maintained until seed is replaced. Trie nodes store the loaded (as exposed from *.sancov.map file) execs/DSOs from target application using the map name as key. Trie node data struct (trieData_t) maintains information for each instrumented map including a bitmap with all hit relative PC addresses (realPC - baseAddr to circumvent ASLR). Map's bitmap is updated while new areas on target application are discovered based on absolute elitism implemented at fuzz_sanCovFeedback(). For individual data files a PID (fuzzer's thread) based filename search is performed to identify all files belonging to examined execution. This method doesn't implement yet bitmap runtime data to detect newly discovered areas. It's mainly used so far as a comparison metric for raw-unpack method and stability check for sancov experimental features such as coverage counters: http://clang.llvm.org/docs/SanitizerCoverage.html Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
* sancov:
SIGABRT is not a monitored signal (thus 'abort_on_error' is missing crashes when set) for Android OS since it produces lots of useless crashes due to way Android process termination hacks work. Safest option is to register & monitor one of user signals. SIGUSR2 is used for sanitizer fuzzing in Android, although might need to be changed if target uses it for other purposes. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
* sancov: make depend
ASan exitcode flag used in Android due to unmonitored SIGABRT, doesn't raise any signals. Thus needs to be treated at the target pid exit code level. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
In order to have accurate coverage data to work against the first iteration of a new seed pickup is not mangled. This will save the coverage bitmaps of original input. In case of multiple worker threads, only one picks this tasks and keeps a lock until finished, blocking other threads from continuing fuzzing. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Also updated crash data analysis when based on exit codes instead of raised signal. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Add global string buffers to store the dynamically constructed sanitizer flags based on invocation arguments. Buffers are initialized once during LINUX arch init, avoiding performance overhead on each child spawn. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
For sanitizer enabled targets with 'abort_on_error' set, the number of major frames needs to increased since the top 7-9 frames are occupied with sanitizer internal symbols. Is sanitizer enabled targets major frames are increased to 14 preventing possible unique crashes from getting lost. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Log error of bitmap overflow so that error can be tracked and increase size if necessary for specific targets. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Crashing PC, address, type of error & stack frames parsed from ASan report files. Generated reports and crash filenames have been updated keeping format compatibility with signal detected crashes. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
For Linux arch where abort_on_error is enabled, don't save report files since they're not parsed thus never deleted (polluting the workdir). Also fixed a small typo in MSAN flags. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Since both crash address & call stack hashes are available, apply filters for ignore addresses & blacklisted hashes. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Increase crashes counter maintained for each new seed pick-up from initial input corpus when ASan report parsing method is triggered to process detected crashes. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Renamed data structs, counters & printed information in order to be technically more accurate based on instrumentation techniques offered by clang sanitizers. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
When perf feedback is enabled, user is allowed to provide an empty input corpus resulting into fuzzer working in discovery mode utilizing perf counters. Update 1st round of empty seed checks to be aligned with revised blocking thread logic. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Instead of making SIGABRT monitor Android specific, define a global flag at common header to control SIGABRT monitor and adjust sanitizers' abort_on_error flag accordingly. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Since sanitizer flags are always set without prior knowledge if target is sanitizer compiled or not, always increase number of major frames if SIGABRT is monitored. Maybe export an additional argument in future, but for now seems good enough. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Separate post crash execution actions for main workers and other (e.g. verifier) when exit code crash is detected. Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Even the smallest comment that might have some performance improvement at sancov parsing or book keeping is super welcome. Evolution seed queues are in the todo list for the near future to get the best out of the coverage bitmaps. |
Bug was introduced while resolving getdelim memory leaks at commit 3a8e16f Signed-off-by: Anestis Bechtsoudis <anestis@census-labs.com>
Amazing work, thanks! |
robertswiecki
added a commit
that referenced
this pull request
Jan 14, 2016
Add sanitizer coverage feedback evolution support part2
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
This a follow up for #44. Clang's sancov data parsing component has been upgraded with runtime coverage bitmaps aiming to identify newly discovered execution paths. Initial sancov feedback parsing was relying solely on maximum global coverage counters for seeds evolution without considering newly discovered basic blocks that don't improve global counters. This PR is introducing a series of upgrades into that direction by implementing fundamental sancov runtime data parsing procedures & book keeping runtime structures.
Following paragraphs provide an executive summary of involved commits. Additional, details are available at each commit's description and accompanied source code comments.
SanCov upgrades
Supported methods
Clang sanitizers' coverage data can be exported with two methods based on environment variable flags:
Both methods are supported from sancov parsing component, although the later is preferred for performance (reduce File I/O & discovery logic) and efficiency (if sanitizer unhandled signal, such as SIGKILL, raised coverage data are lost - huge problem for Android) reasons. It should be also noted that coverage bitmaps are supported only for the later due to huge performance overhead when parsing data produced with first method. As such, when individual files are selected (
coverage_direct
flag disabled) only global coverage counters are supported without using BB bitmaps.Parsing coverage data
When sanitizer coverage is enabled, compiler is injecting a call for the
__sanitizer_cov
procedure at every basic block identified. Runtime collected coverage data are then stored into generated files from__sanitizer_cov_dump
procedure invoked as part of sanitizer'sDie
steps. Detected hit BB addresses are absolute, thus they need to be adjusted to relative before updating fuzzer's coverage bitmaps (that randomization bits again). This is accomplished by parsing thepid.sancov.map
file which contains a minimized version of ProcFS maps that can be used to calculate BB relative addresses using matching map's base address.When coverage feedback is enabled with raw unpack method, a digital tree (trie) is generated whenever a new input seed is selected for evolution. Loaded executable/DSO map names (as parsed from
pid.sancov.map
) are used as keys for the trie node actions (insert, search). Additionally, for each node where the matching exec/DSO has coverage instrumentation enabled, a coverage bitmap is allocated and maintained in parallel with trie lifetime. Trie and bitmaps are shared across the fuzzer threads, with each thread (roughly) executing the following steps when parsing coverage data:pid.sancov.map
filepid.sancov.raw
fileParsing component takes advantage of addresses locality (next BB addr most probably fits into same map entry) to minimize search overhead by caching last used map entry & trie node. Additionally, a small sorted lookup array of maps' base addresses is generated to speed up the maps index search step when parsing raw files.
Interaction with global data structures is mutex protected to avoid races. Bitmap updates occur in a first-come-first-serve nature ensuring that new BB hits are measured only once from actively running workers.
Evolution metrics
For evolution decisions (discard/keep mutated test cases)
fuzz_sanCovFeedback
is evaluating worker thread's coverage based on:For rule
1.
the second part of the expression has been temporarily introduced until test case queues are implemented. Currently honggfuzz is using absolute elitism, promoting only one test case (the best) to next iterations. In that case if newly discovered paths are blindly followed without considering global coverage metrics, there's a higher propability that fuzzer enters a dead-end state where target always early aborts. This is a very common case for media decoders/parsers where a potentially malformed chunk triggers additional evaluation/error-handling exec paths, which effectively results into new BB hits. However, such exec path is most probably triggering an early abort call due to malformed data trapping the fuzzer into a dead-end that might never escape until initial seed is replaced due to expiration. When evolution seed queues are implemented previous metrics will be revised.First execution of new seed selection
The 1st iteration of a newly selected seed from input corpus is perfomed without any content mangling taking place. This will effectively set the initial coverage data with base metrics required to compare against at following evolution. Additionally, to avoid races between worker threads that might result into inaccurate base data creation, only one thread is executing this first round blocking the other threads from continuing before all initial data structures updates are completed.
SanCov display sample
Linux: fuzzing with sanitizers
exitcode/SIGABRT monitoring
A common practise when fuzzing with sanitizers enabled for target application, is to set the
abort_or_error
flag. This will effectively result into a SIGABRT being raised when sanitizer detects an error. If SIGABRT is a monitored signal from fuzzer's ptrace API, crash detection logic will identify the interesting signal and proceed with unwinding & post crash actions.For some targets SIGABRT monitoring is not desired resulting into
abort_or_error
flag being useless since some detected errors might get lost. In these case a custom exitcode is registered and monitored per enabled sanitizer (ASan, MSan, UBSan). If ptrace API detects a PID exit with monitored exitcodes, additional crash processing actions are triggered that parse the sanitizer saved report file.linux/arch.c
initialization routines detect if SIGABRT is a monitored signal based on common header config and adjust sanitizer flags and reports generation accordingly. Crash counters, uniqueness decissions (including blacklist) and crashes verifier actions have been updated to support for this new crash monitor method.Additionally, for sanitizers that include unwinded stack traces into reports (such as ASan), crashing thread's trace is parsed to calculate stack hashes benefiting when detecting duplicates and verifying crashes behavior.
Since sanitizer flags are now defined dynamically based on input arguments,
linux/arch.c
init procedure is generating the appropriate flags once and stores them in global buffers that can be instantly accessed from worker threads when spawning new processes.Number of major frames
Originally top 7 frames from call stack where used to calculate hash signature. When ASan fuzzing enabled and
abort_or_error
flag set, the top 6-9 frames are usually occupied with sanitizer internal procs, resulting into crashes being wrongly marked as duplicates and thus being lost. As such when these two conditions are met, arch init procedure is increasing the default 7 major frames to 14, mitigating the problem.exitcode detected crash report sample
Misc improvements