Not only about C++.
Mostly not about C++.
— is testing with random data or conditions..
First we start with a program, we give it a bunch of garbage as input
Or forced to work in garbage conditions.
What we check:
— the program does not crash;
— does not go into loop, and run for too long;
— does not eat up too much memory;
— internal invariants are not violated;
— sanitizers do not find errors.
— A «gold mine» for finding bugs
(where fuzzing is not yet used).
Do you have a parser, converter, compression algorithm?
And you don't use fuzzing yet???
There are so many ways to do Fuzzing.
The more, the better :)
Unstructured:
— testing with random bytes;
Structured:
— testing by a random
structurally correct input.
— random data without feedback (stupid);
— coverage guided (smart);
— with a logical solvers (very smart).
Black box:
— we test the finished binary or service without changes;
With instrumentation:
— rebuilding the program for testing purpose.
By input data:
— we give the program a bunch of garbage as input;
By settings and environment:
— turn on random feature flags;
— change the system time (libfaketime) and timezones;
— Chaos Monkey;
By the way the program is executed::
— check the random order of execution of threads;
— we cause hangups of servers in a distributed environment.
Logical:
— check that the behavior on random data is correct;
— comparisons with another implementation; comparisons with the standard; checking invariants.
Physical:
— the result of the program is not important;
— we check that the program does not crash, sanitizers do not throw error, etc.
Example of successful manual fuzzing:
— kids cracked lock screen in Linux Mint:
Linux Mint fixes screensaver bypass discovered by two kids.
Example: Linux kernel, Syzcaller tool:
— Found more than 3000 bugs, 1056 pending fixes:
https://syzkaller.appspot.com/upstream.
Example: Chromium, ClusterFuzz tool, over 20,000 bugs!
An example where fuzzing would help a lot:
— iPhone crash when displaying
a combination of characters from the Telugu language:
జ్ఞా
The more fuzzing, the better!
Some of them are dumb and primitive (ad-hoc fuzzer).
«It doesn’t matter what the fuzzer looks like, the main thing is that it catches bugs»
— Deng Xiaoping.
http://en.naipo.com/Portals/0/web_en/Knowledge_Center/Feature/IPNE_170224_0701.htm
$expression_cast = {
'CAST' => sub { my ($state) = @_; '(CAST((' . one_of($state, $expression_cast) . ') AS ' . one_of($state, $type_cast) . '))' },
'SELECT' => sub {
my ($state) = @_;
list_of(
$state, {max => 2},
[sub { '( ' . one_of($state, $query_select) . ' ) AS ' . rand_word() }, sub { '( ' . one_of($state, $query_select) . ' ) ' }]
);
},
'number' => sub { my ($state) = @_; return rand_pick(['', '-']) . rand_word(8, 0 .. 9) . rand_pick(['', '.' . rand_word(6, 0 .. 9)]) },
'string' => sub {
my ($state) = @_;
return q{'} . rand_word(8, map { $_ ~~ q{'} ? '\\' . $_ : $_ } map {chr} 32 .. 127) . q{'};
},
'[]' => '[]',
'[x]' => sub { my ($state) = @_; return '[' . one_of($state, $expression) . ']' },
'function()' =>
sub { my ($state) = @_; return one_of($state, $functions) . '(' . list_of($state, {min => 0, max => 3}, $expression) . ')' },
"'\\0'" => "'\\0'",
"''" => "''",
'NULL' => 'NULL',
};
Perl script that generates SQL queries
with random expressions.
https://github.com/ClickHouse/ClickHouse/pull/3442
What did it find?
— lack of checking for boundaries in the H3 library.
Runs all ClickHouse tests in parallel,
in random order, but does not check their result.
Tests interfere with each other:
they create and delete identical tables, etc.
We launch 5 options: with Debug, ASan, MSan, TSan, UBSan.
We check that:
— the server does not fall;
— no deadlocks (hanging requests);
— the server can successfully restart after the test is completed;
https://github.com/ClickHouse/ClickHouse/pull/3057
https://github.com/ClickHouse/ClickHouse/pull/3438
What does it find?
— bugs in RocksDB:
facebook/rocksdb#7711 — race condition;
facebook/rocksdb#7821 — use after free;
facebook/rocksdb#7778 — misc.
— race conditions in NuRaft, rdkafka, AMQP-CPP...
— incredible bug in simdjson: simdjson/simdjson#169.
— race condition in boost? ClickHouse-Extras/boost#8
— bug in LLVM libunwind https://bugs.llvm.org/show_bug.cgi?id=48186
And of course, there are many potential and real bugs in ClickHouse,
most of them before the release.
Added to ClickHouse by Eldar Zaitov.
libfuzzer — coverage guided fuzzer available from LLVM/clang.
-fsanitize=fuzzer,...
-fprofile-instr-generate -fcoverage-mapping
extern "C"
int LLVMFuzzerTestOneInput(const uint8_t * data, size_t size)
{
DB::Lexer lexer(data, data + size);
while (true)
{
DB::Token token = lexer.nextToken();
...
It is recommended to enclose a «corpus» of examples of valid data.
What does it find?
Buffer overrun in data decompression:
https://github.com/ClickHouse/ClickHouse/pull/8404
Exponential backtracking in the parser:
SELECT fo,22222?LUTAY(SELECT(NOT CAUTAY(SELECT(NOT CAST(NOTT(NOT CAST(NOT NOT LEfT(NOT coARRAYlumnsFLuTAY(SELECT(NO0?LUTAY(SELECT(NOT CAUTAY(SELECT(NOT CAST(NOTT(NOT CAST(NOT NOT LEfT(NOT coARRAYlumnsFLuTAY(SELECT(NOTAYTAY(SELECT(NOTAYEFAULT(fo,22222?LUTAY(%SELECT(NOT CAST(NOT NOTAYTAY(SELECT(NOTAYEFAULT(fo,22222?LUTAY(SELECT(NOT CAST(NOT NOT (NOe)))))))))))))))))))))))))))))))))
Not to be confused with Thread Sanitizer.
Race conditions are reproduced in
a specific thread order.
How to test as many different random execution orders as possible?
— We need to switch streams as often as possible
at random times.
But how to do that?
1. Set the signal on the timer: setitimer.
2. Put hooks on pthread_mutex_lock, pthread_mutex_unlock functions.
In the handler, we toss a coin and do:
1. sched_yield.
2. sleep for a random time.
3. sched_setaffinity on a random CPU.
Works great with Thread Sanitizer and Stress Test!
And he also finds a bunch of flaky tests
(race not in the program but in tests).
https://github.com/ClickHouse/ClickHouse/blob/master/
src/Common/ThreadFuzzer.h, cpp
In clickhouse-client we take requests from regression tests.
From parsed requests, we take pieces of AST (Abstract Syntax Tree).
We substitute them randomly in other queries.
Mutating string literals...
We execute... the result is not important, we check that the server has not crashed.
Five options to test with: Debug, ASan, MSan, TSan, UBSan.
SELECT 0.00009999999747378752 * NULL, 1 * -2, 1024, sum(toUInt64(255, 1048576 * 65537, NULL * 100.0000991821289)) FROM (SELECT 1 * 1025, intDiv(number, 1.) AS k, toUInt64('0.0000065536', 255, toUInt64(7 * NULL), NULL * 0.) FROM numbers(2 * 9223372036854775807, toUInt64(10 * 255)) GROUP BY k)
https://github.com/ClickHouse/ClickHouse/pull/12111
Discovery of the year! Over 200 issues:
https://github.com/ClickHouse/ClickHouse/issues?q=is%3Aissue+label%3Afuzz
Not only bugs: edge cases, UBSan triggers...
SELECT bar((greatCircleAngle(100, -1, number, number) - number) * 2, -9223372036854775808, 1023, 100) FROM numbers(1048575);
Errored in UBSan, ASan tests... Somewhere it turns out NaN -> checks for array boundaries -> buffer overflow.
Bonus: Aggressive phasing of new tests in pull requests.
Tests new code for edge cases,
even when the author didn't think about it.
Developed by Manuel Rigger at ETH Zurich.
Added to ClickHouse by Ilya Yatsishin.
Generates random valid SQL queries and data.
Verifies that the result is the same as a reference unoptimized query engine written according to the SQL standard.
Example:
SELECT t0.c2, t0.c1, t0.c0 FROM t0 WHERE t0.c0 ORDER BY ((t0.c2)>=(t0.c1)), (((- (((t0.c0)>(t0.c0))))) IS NULL) FORMAT TabSeparatedWithNamesAndTypes;
Insufficient column type check in WHERE, result in segfault.
zzuf — mutates data when working with files and the network.
fuzzgrind — dynamic translation + solver for finding data that passes conditions in machine code (project abandoned).
AFL — Instrumentation builds are recommended, similar to libfuzzer, but run outside of the process.
Hongfuzz — supports hardware counters for coverage.
Radamsa — is not a fuzzer, but a separate data mutator.
Service security scanners are also phasers, for example: sqlmap.
You have to use Fuzzing. Everywhere, as much as possible!
We use fuzzer in CI — for pull request and for commit.
Need more fuzzer.
Bugs shall not pass!