Audit Fuzzer Adalogics 2021
Audit Fuzzer Adalogics 2021
in collaboration with
Authors
David Korczynski <[email protected]>
Adam Korczynski <[email protected]>
Date: 29th april, 2021
Ada Logics
London, United Kingdom
1
Envoy fuzzing improvements, April 2021
Table of Contents
Executive summary 3
3 Future advice 18
4 Conclusions 19
5 Appendix 20
A.0 Disassembly of empty fuzzers 20
A.1 Build configurations 22
A.2 Envoy UDP fuzzer coverage 26
Ada Logics
London, United Kingdom
2
Envoy fuzzing improvements, April 2021
Executive summary
In this engagement we performed two tasks. The first was optimising the performance of
end-to-end fuzzers and the second was developing a fuzzer for the UDP listener code of
Envoy.
Regarding optimisation, we find that the cause of performance issues in the end-to-end
fuzzers is due to the large amount of code that is instrumented for coverage-guiding the
Envoy fuzzers. Specifically, the Envoy fuzzers run with a large amount of inline 8-bit
counters which is a counter inserted by SanitizerCoverage on every edge of the target
application. libFuzzer uses the counters by iterating through all of the counters after each
fuzz iteration in order to measure code coverage. Thus, the more counters there are, the
more effort libFuzzer has to spend iterating through all counters on each fuzz iteration. We
show how this impacts Envoy performance in four different ways:
1. The exact same empty fuzzer compiled with fuzzer instrumentation runs 419 times
slower when also compiled with Envoy instrumentation.
2. The h2_capture_persistent_fuzz_test fuzzer spends only 20% of the actual execution
inside LLVMFuzzerTestOneInput.
3. Profilers reveal an excessive amount of time is spent inside coverage-collection
code.
4. The end-to-end fuzzers contain around 1.2 million inline 8-bit counters, which is huge
in comparison to other fuzzers, e.g. Lua fuzzers contain 6000 inline 8-bit counters.
We show how limiting the coverage instrumentation improves fuzzer performance and
observe the same effect on OSS-Fuzz statistics.
Regarding UDP fuzzing, we develop a UDP fuzzer that targets the code requested by the
Envoy team, namely handleReadCallback. We demonstrate code coverage by observing the
target code in OSS-Fuzz reports, and also demonstrates that the fuzzer catches the DOS
issue previously reported.
In this report we go through both of these tasks and spend considerable detail on the
coverage instrumentation part.
Ada Logics
London, United Kingdom
3
Envoy fuzzing improvements, April 2021
Executive summary:
The instrumentation of Envoy has a significant slowdown of fuzzer execution. For example, a
fuzzer with a single branch runs 419 times slower with Envoy instrumentation than without.
Limiting the amount of instrumentation used in the Envoy project shows to increase
performance of the fuzzers. This has been added to the OSS-Fuzz build of Envoy, which has
shown to increase performance on the OSS-Fuzz logs. There is future work for the Envoy
maintainers on refining the instrumentation of the fuzzers.
Ada Logics
London, United Kingdom
4
Envoy fuzzing improvements, April 2021
The way libFuzzer uses these 8bit-counters is by iterating through all of them, i.e. iteration
through a sequence of counters corresponding to the number of edges in the target
application, after each fuzz iteration in order to determine if new coverage has been
achieved.
ExecuteCallback(Data, Size);
auto TimeOfUnit = duration_cast<microseconds>(UnitStopTime - UnitStartTime);
UniqFeatureSetTmp.clear();
size_t FoundUniqFeaturesOfII = 0;
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
TPC.CollectFeatures([&](uint32_t Feature) {
if (Corpus.AddFeature(Feature, static_cast<uint32_t>(Size), Options.Shrink))
UniqFeatureSetTmp.push_back(Feature);
if (Options.Entropic)
Corpus.UpdateFeatureFrequency(II, Feature);
if (Options.ReduceInputs && II && !II->NeverReduce)
if (std::binary_search(II->UniqFeatureSet.begin(),
II->UniqFeatureSet.end(), Feature))
FoundUniqFeaturesOfII++;
});
This code snippet has two purposes. First, to run ExecuteCallback which calls into the
fuzzing entry point function (LLVMFuzzerTestOneInput). Second,
to gather the code coverage of the target follow the fuzzer’s single execution, which
is done by TPC.CollectFeatures. The TPC.CollectFeatures code begins as follows:
Ada Logics
London, United Kingdom
5
Envoy fuzzing improvements, April 2021
size_t FirstFeature = 0;
The nested for loop calls into ForEachNonZeroByte with two pointers as the first
two arguments. These arguments point to the areas in the code where a page of
inline 8-bit counters exist, namely, each region in the Modules vector encapsulates
a page of 8bit counters. The implementation of ForEachNonZeroByte is defined here
https://github.com/llvm/llvm-project/blob/e60d6e91e196d91a1b9bfcc93d9f43946ea29299/co
mpiler-rt/lib/fuzzer/FuzzerTracePC.h#L184 and essentially the function will call the callback
(Handle8bitCounter) for each nonzero byte in the memory region. In the case of the
RunOne function the result is to call into the function provided by the call to
TPC.CollectFeatures, which eventually calls into various functions related to the corpus,
e.g. Corpus.AddFeature and Corpus.UpdateFeatureFrequency which will trigger
changes in the Corpus representation if the features indicate that a new piece of code has
been executed.
The main point to get across in the above section is that nested for-loop in
CollectFeatures iterates through the regions with inline 8-bit counters, and what we will
observe in the Envoy fuzzers is that the number of inline 8-bit counters is significantly large.
only iterates through the inline 8-bit counters if a given region is “Enabled”. Unfortunately,
the Enabled boolean will always be true for all regions as all regions are initialised to true
Ada Logics
London, United Kingdom
6
Envoy fuzzing improvements, April 2021
(https://github.com/llvm/llvm-project/blob/ab5823867c4aee7f3e02ddfaa217905c87471bf9/co
mpiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L59) and never set to false.
#include "test/integration/h2_fuzz.h"
DEFINE_FUZZER(const uint8_t* buf, size_t len) {
if (len == 1234123412) {
std::cout << "hello " << buf << "\n" ;
}
}
envoy_cc_fuzz_test(
name = "h2_empty_fuzz_test",
srcs = ["h2_empty_fuzz_test.cc"],
copts = ["-DPERSISTENT_FUZZER"],
corpus = "h2_corpus",
deps = [":h2_fuzz_persistent_lib"],
)
We then compile and run the fuzzer, and observe the following output:
$ h2_empty_fuzz_test
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2391912932
INFO: Loaded 1 modules (1282530 inline 8-bit counters): 1282530 [0xa51f110,
0xa6582f2),
INFO: Loaded 1 PC tables (1282530 PCs): 1282530 [0xa6582f8,0xb9ea118),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096
bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 3 ft: 4 corp: 1/1b exec/s: 0 rss: 170Mb
Ada Logics
London, United Kingdom
7
Envoy fuzzing improvements, April 2021
#8192 pulse cov: 3 ft: 4 corp: 1/1b lim: 80 exec/s: 4096 rss: 170Mb
#16384 pulse cov: 3 ft: 4 corp: 1/1b lim: 163 exec/s: 4096 rss: 171Mb
#32768 pulse cov: 3 ft: 4 corp: 1/1b lim: 325 exec/s: 4096 rss: 172Mb
#65536 pulse cov: 3 ft: 4 corp: 1/1b lim: 652 exec/s: 3855 rss: 175Mb
If we create the same empty fuzzer but outside the envoy build environment but following the
Envoy fuzzer set up
https://github.com/envoyproxy/envoy/blob/1d1b708c7bf6efa02c41d9ce22cbf1e4a1aeec2c/te
st/fuzz/fuzz_runner.h#L71:
#include <iostream>
#define DEFINE_TEST_ONE_INPUT_IMPL \
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { \
EnvoyTestOneInput(data, size); \
return 0; \
}
#define DEFINE_FUZZER \
static void EnvoyTestOneInput(const uint8_t* buf, size_t len); \
DEFINE_TEST_ONE_INPUT_IMPL \
static void EnvoyTestOneInput
Ada Logics
London, United Kingdom
8
Envoy fuzzing improvements, April 2021
The execution speed and the number of inline 8-bit counters are very different in the two
cases. Specifically, we extract two important conclusions from the above observations
1. The fuzzer that is compiled outside of the Envoy fuzzer environment executes with
roughly 1.67 million executions per second, whereas the one in the Envoy
environment executes with roughly 4000 executions per second. The envoy
instrumentation slows down the fuzzer by roughly 419x.
2. The number of inline 8-bit counters in the Envoy fuzzer is 1282530 whereas it is 126
in the empty fuzzer (these numbers can be observed by the output from LibFuzzer
given above). This corresponds to iterating through 126 bytes versus 1282530 bytes
after each fuzz iteration.
inside the ForEachNonZeroByte function. In addition to this, looking at the flame graphs
provided by Prodfiler we see that around 19% of the entire machine execution was spent
inside of the CollectFeatures function.
Ada Logics
London, United Kingdom
9
Envoy fuzzing improvements, April 2021
1.2.3 Using fuzzer instrumentation to assess the overall time spent with
fuzzing.
Another thing we were interested in understanding was how much of the fuzzing execution
was actually spent in the target and how much was spent in the fuzzing engine itself. To do
this, we added some simple instrumentation to the h2_capture_fuzz.cc and h2_fuzz.cc files,
such that the h2_capture_fuzz fuzzer will log timestamps as follows:
DEFINE_PROTO_FUZZER(..H2CaptureFuzzTestCase input)
ADD_TIME_STAMP(F1)
H2FuzzIntegrationTest h2_var;
h2_var.replay(input)
--> H2FuzzIntegrationTest::replay(... H2CaptureFuzzTestCase& input,)
...
ADD_TIME_STAMP(R4)
IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("http"));
...
ADD_TIME_STAMP(R5)
for (int i = 0; i < input.events().size(); ++i) {
if (stop_further_inputs) {
break;
}
INC_GLOBAL(PACKET_COUNT)
// send packet
} // endloop
ADD_TIME_STAMP(R6)
...
<--
ADD_TIME_STAMP(F2)
<--
With these timestamps available we set up an experiment to measure the following metrics:
Ada Logics
London, United Kingdom
10
Envoy fuzzing improvements, April 2021
Metric Result
Total execution time 1200 sec
F1-F2 244 sec
R4-R6 239 sec
R5-R6 226 sec
F1-F2/Total time 20%
R4-R6/Total time 19.9%
R5-R6/Total time 18.8%
Total number of client fuzz inputs 82797
Total fuzz inputs to client / (F1-F2) 339 inputs/sec
Total fuzz inputs to client / (R4-R6) 346 inputs/sec
Total fuzz inputs to client / (R5-R6) 366 inputs/sec
Total number of fuzz iterations 27761
We observe that only 20% of the fuzz time is spent in the actual fuzzing code, and thus 80%
of the time is spent in the libFuzzer engine.
1.2.4 Counting the number of 8-bit inline counters in each Envoy fuzzer.
Finally, we wanted to understand how many 8-bit inline counters are actually used by the
Envoy fuzzers. This number is reported by the fuzzers when they are initiated, so the only
thing we have to do in this case is build the fuzzers, launch them and then count the number
of inline 8-bit counters. We do this using a build with AddressSanitizer, and we see the
following results:
h2_capture_persistent_fuzz_test 1281655
h2_capture_fuzz_test 1281649
Ada Logics
London, United Kingdom
11
Envoy fuzzing improvements, April 2021
h1_capture_fuzz_test 1268160
h1_capture_persistent_fuzz_test 1268166
codec_fuzz_test 1030067
hash_fuzz_test 570594
get_sha_256_digest_fuzz_test 579995
evaluator_fuzz_test 1100639
dns_filter_fuzz_test 1139928
For comparison purposes the following table gives examples of 8-bit counters in various
open source projects that we have fuzzed.
Lua 6322
PugiXML 4871
LevelDB 5948
RocksDB 137913
It is clear that the number of inline 8-bit counters in Envoy is significantly larger than any of
the other projects. For example, our fuzzer targeted the core library (liblua.a) for the Lua
programming language has a total of 6322 inline 8-bit counters whereas the h2 end-to-end
Envoy fuzzer has a total of 1.2 million.
1.2.5 Conclusions
The instrumentation set up in Envoy is a significant performance bottleneck for the fuzzers.
The main reason for the performance is the post-processing that occurs after each fuzz
execution in terms of collecting coverage features to assess whether the execution
discovered new code execution. An empty fuzzer without Envoy instrumentation runs 419x
faster than an empty fuzzer with Envoy instrumentation.
We want to emphasize here that the fuzzing infrastructure of Envoy is of high quality and the
team behind it has shown high competencies in fuzzing Envoy. In fact, from a certain
perspective it is positive that Envoy builds all code related to Envoy with sanitizers and this
should be considered a great achievement as well as having the right intentions. It is due to
the sheer amount of code in the resulting Envoy binaries, which is due to linking of many
third-party libraries in addition to the large Envoy code base in and of itself, that the
coverage collection ends up having a significant slowdown. In the vast majority of projects
we would not advise limiting the coverage instrumentation across a fuzzing set up.
Ada Logics
London, United Kingdom
12
Envoy fuzzing improvements, April 2021
Furthermore, the observations we made in this section are not common knowledge and it is
not to be expected from non-fuzzing experts to consider this during fuzzer development.
-fsanitize-coverage=0
-fno-sanitize=all
Bazel builds: First, we tried controlling instrumentation details inside of the Envoy BUILD
scripts. In particular, adding `copts` definitions to various places in the BUILD scripts of
ENVOY. This works for the majority of bazel rules, however, it does not work for any rule
defined by way of proto_library. This is because proto_library does not allow us to
propagate compilation options to the protobuf compiler, and thus it does not allow us to
include the flags to disable instrumentation on a per-target basis. We reference to issues in
the bazelbuild/rules_proto repository here:
https://github.com/bazelbuild/rules_proto/issues/85
https://github.com/bazelbuild/rules_proto/issues/41
Passing flags to clang: Second, we tried using the `--per_file_copt` command line
option for bazel builds. This option accepts a regular expression that encapsulates a set of
files used during compilation as well as a string that will be passed to the compiler during
compilation of files that match the regular expression. For example, the following line:
--per_file_copt=^.*\.pb\.cc$@-fsanitize-coverage=0,-fno-sanitize=all
will pass the flags -fsanitizer-coverage=0 and -fno-sanitize=all to all files compiled
in the bazel build with .pb.cc extensions. This option worked well.
Ada Logics
London, United Kingdom
13
Envoy fuzzing improvements, April 2021
In total, we compiled the Envoy fuzzers with 6 different configurations and the configurations
are shown in Appendix A.1. The following table shows the results.
Fuzzer Setting inline 8-bit Total execs Exec/sec
counters (300 sec)
Ada Logics
London, United Kingdom
14
Envoy fuzzing improvements, April 2021
(includes
blocked list of
functions)
Finally, we note that the executions per second increases if the fuzzer is run for a longer
period of time. For example, in our experiments we have observed above 100 exec/sec in
the http2 end-to-end fuzzer with seeds in longer fuzz runs. The data of these runs have been
shared with the Envoy team internally.
Deploying on OSS-Fuzz.
We integrated some of the above efforts into the OSS-Fuzz build script of Envoy and the
following figure shows impact.
We can see here that a performance improvement happened on the 16th April 2021.
Inspecting the logs, we notice on the 15th April the amount of inline 8-bit counters was
~600,000
(https://console.cloud.google.com/storage/browser/_details/envoy-logs.clusterfuzz-external.a
ppspot.com/libFuzzer_envoy_h2_capture_persistent_fuzz_test/libfuzzer_asan_envoy/2021-
04-15/00:06:53:330737.log) whereas on the 16th April it was ~300,000
(https://console.cloud.google.com/storage/browser/_details/envoy-logs.clusterfuzz-external.a
ppspot.com/libFuzzer_envoy_h2_capture_persistent_fuzz_test/libfuzzer_asan_envoy/2021-
04-16/01:54:36:943426.log). This decrease in inline 8-bit counters resulted in a 3x
improvement of the fuzzer execution. We note here that in addition to the decrease in inline
8-bit counters the stability of the fuzzer was also improved on the same day through bug
fixing, which may have impacted the performance as well.
Ada Logics
London, United Kingdom
15
Envoy fuzzing improvements, April 2021
The goal of the fuzzer is to send an arbitrary number of arbitrary packets to the Envoy UDP
listener. To do this we rely on the primitives provided by Network::Test::UdpSyncPeer and
the core of the fuzzer is encapsulated by the following loop
(https://github.com/envoyproxy/envoy/blob/58a13570f3f4dea9bad8b8fa5e1221d7ed5056de/t
est/common/network/udp_fuzz.cc#L92):
The fuzzer supports communicating by way of GRO, MMSG as well as regular recvmsg, which
is controlled by the following code
(https://github.com/envoyproxy/envoy/blob/58a13570f3f4dea9bad8b8fa5e1221d7ed5056de/t
est/common/network/udp_fuzz.cc#L73):
In order to verify the fuzzer finds the original crash of the code we ran the fuzzer
Ada Logics
London, United Kingdom
16
Envoy fuzzing improvements, April 2021
against the Envoy codebase with the changes applied by the commit that fixed the
UDP bug (https://github.com/envoyproxy/envoy/pull/14122/files). More specifically,
if we avoid the use of the condition
if (output.msg_[i].truncated_and_dropped_) {
continue;
}
in this commit
https://github.com/envoyproxy/envoy/pull/14122/files#diff-c9702469f313c70572e261f4af736
87873ab8247264eb47fd05300098067c5abR632 then we get the following result:
$ /out/udp_fuzz ./seeds/
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 185475315
INFO: Loaded 1 modules (1087563 inline 8-bit counters): 1087563 [0x861fcb0,
0x87294fb),
INFO: Loaded 1 PC tables (1087563 PCs): 1087563 [0x8729500,0x97c19b0),
INFO: 55 files found in ./seeds/
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096
bytes
INFO: seed corpus: files: 55 min: 1b max: 2251b total: 4519b rss: 150Mb
AddressSanitizer:DEADLYSIGNAL
=================================================================
==28367==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc
0x000005872691 bp 0x7fff415ade70 sp 0x7fff415ad980 T0)
==28367==The signal is caused by a READ memory access.
==28367==Hint: address points to the zero page.
#0 0x5872691 in Envoy::Network::passPayloadToProcessor(unsigned long,
std::__1::unique_ptr<Envoy::Buffer::Instance,
std::__1::default_delete<Envoy::Buffer::Instance> >,
std::__1::shared_ptr<Envoy::Network::Address::Instance const>,
std::__1::shared_ptr<Envoy::Network::Address::Instance const>,
Envoy::Network::UdpPacketProcessor&,
std::__1::chrono::time_point<std::__1::chrono::steady_clock,
std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l> > >)
/proc/self/cwd/source/common/network/utility.cc:562:3
#1 0x587501e in Envoy::Network::Utility::readFromSocket(Envoy::Network::IoHandle&,
Envoy::Network::Address::Instance const&, Envoy::Network::UdpPacketProcessor&,
std::__1::chrono::time_point<std::__1::chrono::steady_clock,
std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l> > >, bool,
unsigned int*) /proc/self/cwd/source/common/network/utility.cc:669:7
#2 0x5877d3a in
Envoy::Network::Utility::readPacketsFromSocket(Envoy::Network::IoHandle&,
Envoy::Network::Address::Instance const&, Envoy::Network::UdpPacketProcessor&,
Envoy::TimeSource&, bool, unsigned int&)
/proc/self/cwd/source/common/network/utility.cc:702:38
#3 0x552b14d in Envoy::Network::UdpListenerImpl::handleReadCallback()
/proc/self/cwd/source/common/network/udp_listener_impl.cc:75:34
#4 0x552a15f in Envoy::Network::UdpListenerImpl::onSocketEvent(short)
/proc/self/cwd/source/common/network/udp_listener_impl.cc:64:5
Ada Logics
London, United Kingdom
17
Envoy fuzzing improvements, April 2021
#5 0x5531e26 in
Envoy::Network::UdpListenerImpl::UdpListenerImpl(Envoy::Event::DispatcherImpl&,
std::__1::shared_ptr<Envoy::Network::Socket>, Envoy::Network::UdpListenerCallbacks&,
Envoy::TimeSource&, envoy::config::core::v3::UdpSocketConfig
const&)::$_0::operator()(unsigned int) const
/proc/self/cwd/source/common/network/udp_listener_impl.cc:38:53
….
….
To confirm the fuzzer targets the code that was asked for by the Envoy team we observe
coverage of the fuzzer in OSS-Fuzz. Consider the following links showing the UDP Listener
code is being analysed:
● Envoy::Network::UdpListenerImpl::handleReadCallback
https://storage.googleapis.com/oss-fuzz-coverage/envoy/report
s/20210423/linux/proc/self/cwd/source/common/network/udp_list
ener_impl.cc.html#L72
● Envoy::Network::Utility::readFromSocket
https://storage.googleapis.com/oss-fuzz-coverage/envoy/reports/20210423/linux/proc
/self/cwd/source/common/network/utility.cc.html#L576
In addition to this, consider the figures in Appendix A.2 showing the coverage is being
achieved.
3 Future advice
We consider there to be three problems that need solving for the Envoy team.
First, enable the ability to disable instrumentation of protobuf code included in header files,
which in themselves can have a huge amount of code. This is because a lot of protobuf code
is included by way of header files in the Envoy source code:
The unfortunate consequence of this is that the code in the header file could avoid being
instrumented but because it is included by important code that has to be instrumented, the
protobuf code consequently will also be instrumented. Some refactoring that makes it easy
to control the instrumentation parameters of the protobuf code would likely have a large
impact on the execution speed.
Ada Logics
London, United Kingdom
18
Envoy fuzzing improvements, April 2021
This will disable coverage instrumentation for any function that contains nocoveragepls in
its function name, which includes the namespace name. As such, a way to disable
instrumentation in a smart manner is to include a file blocked_list.txt in the Envoy repository,
which we will then use during Envoy fuzzing compilation as follows:
--per_file_copt="-fsanitize-coverage-blocklist=envoy_code/block_list.txt"
We did an initial experimentation of this by way of configuration #6 discussed in section
1.3.2.
Second, add the ability to do per-target instrumentation. It is clear that the fuzz targets in the
Envoy code target separate parts of the code base. Ideally instrumentation should be made
on a target-specific basis. The goal should be to have less than 100,000 inline 8-bit counters
per fuzzer. Notice that the number of counters is shown in the libFuzzer logs (as shown
above section 1.2.1) which are also available on OSS-Fuzz for each of the OSS-Fuzz runs.
Third, during the engagement we observed that several of the fuzzers had a high crashing
percentage (above 90%) for several months. In fact, the http2 end-to-end fuzzer had an
input in its corpus that caused the fuzzer to crash. Effectively, the fuzzer had not explored
new code for months because this input blocked the fuzzer from continuing. We think it is
crucial for the Envoy team to ensure the fuzzers of Envoy are running properly and this
should be considered higher priority than ensuring the fuzzers run fast.
4 Conclusions
In this engagement we improved Envoy fuzzing by identifying the performance bottleneck in
the Envoy fuzzing set up and creating a new UDP fuzzer that targets previously buggy code.
Our findings identify that the performance bottleneck in Envoy is due to large amounts of
code instrumentation which causes LibFuzzer to spend significant effort in determining if
each fuzz iteration caused new code to execute. We show how this affects the Envoy
fuzzers, for example by highlighting an empty fuzzer runs 419 times slower with Envoy’s
instrumentation approach. We show how to limit the amount of instrumentation in Envoy and
how it improves performance.
Ada Logics
London, United Kingdom
19
Envoy fuzzing improvements, April 2021
5 Appendix
A.0 Disassembly of empty fuzzers
Appendix showing the disassembly of LLVMFuzzerTestOneInput in the two empty fuzzers is
similar.
Disassembly of h2_empty_fuzz_test, namely the empty fuzzer compiled with Envoy
instrumentation.
$ gdb /out/h2_empty_fuzz_test
Reading symbols from /out/h2_empty_fuzz_test...done.
(gdb) set disassembly-flavor intel
(gdb) disass LLVMFuzzerTestOneInput
Dump of assembler code for function LLVMFuzzerTestOneInput:
0x0000000003235e90 <+0>: push rbp
0x0000000003235e91 <+1>: mov rbp,rsp
0x0000000003235e94 <+4>: add BYTE PTR [rip+0x72d9dfc],0x1 # 0xa50fc97
0x0000000003235e9b <+11>: mov rax,0xffffffffffffedc8
0x0000000003235ea2 <+18>: cmp rbp,QWORD PTR fs:[rax]
0x0000000003235ea6 <+22>: jae 0x3235eac <LLVMFuzzerTestOneInput+28>
0x0000000003235ea8 <+24>: mov QWORD PTR fs:[rax],rbp
0x0000000003235eac <+28>: call 0x3235ec0 <_ZL17EnvoyTestOneInputPKhm>
0x0000000003235eb1 <+33>: xor eax,eax
0x0000000003235eb3 <+35>: pop rbp
0x0000000003235eb4 <+36>: ret
End of assembler dump.
(gdb) disass _ZL17EnvoyTestOneInputPKhm
Dump of assembler code for function _ZL17EnvoyTestOneInputPKhm:
0x0000000003235ec0 <+0>: push rbp
0x0000000003235ec1 <+1>: mov rbp,rsp
0x0000000003235ec4 <+4>: push r14
0x0000000003235ec6 <+6>: push rbx
0x0000000003235ec7 <+7>: add BYTE PTR [rip+0x72d9dca],0x1 # 0xa50fc98
0x0000000003235ece <+14>: mov rbx,rsi
0x0000000003235ed1 <+17>: mov r14,rdi
0x0000000003235ed4 <+20>: mov rax,0xffffffffffffedc8
0x0000000003235edb <+27>: cmp rbp,QWORD PTR fs:[rax]
0x0000000003235edf <+31>: jae 0x3235ee5 <_ZL17EnvoyTestOneInputPKhm+37>
0x0000000003235ee1 <+33>: mov QWORD PTR fs:[rax],rbp
0x0000000003235ee5 <+37>: mov edi,0x498f3a94
0x0000000003235eea <+42>: mov rsi,rbx
0x0000000003235eed <+45>: call 0x3153860 <__sanitizer_cov_trace_const_cmp8()>
0x0000000003235ef2 <+50>: cmp rbx,0x498f3a94
0x0000000003235ef9 <+57>: jne 0x3235f31 <_ZL17EnvoyTestOneInputPKhm+113>
0x0000000003235efb <+59>: add BYTE PTR [rip+0x72d9d98],0x1 # 0xa50fc9a
0x0000000003235f02 <+66>: lea rdi,[rip+0x908047f] # 0xc2b6388 <_ZNSt3__14coutE>
0x0000000003235f09 <+73>: lea rsi,[rip+0xfffffffffd3ba150] # 0x5f0060 <.str.4>
0x0000000003235f10 <+80>: call 0x3235f40
<_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc>
0x0000000003235f15 <+85>: mov rdi,rax
0x0000000003235f18 <+88>: mov rsi,r14
0x0000000003235f1b <+91>: call 0x3235f80
<_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKh>
0x0000000003235f20 <+96>: lea rsi,[rip+0xfffffffffd3ba179] # 0x5f00a0 <.str.5>
0x0000000003235f27 <+103>: mov rdi,rax
0x0000000003235f2a <+106>: call 0x3235f40
<_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc>
Ada Logics
London, United Kingdom
20
Envoy fuzzing improvements, April 2021
# gdb -q ./a.out
Reading symbols from ./a.out...done.
(gdb) set disassembly-flavor intel
(gdb) disass LLVMFuzzerTestOneInput
Dump of assembler code for function LLVMFuzzerTestOneInput:
0x0000000000557d40 <+0>: push rbp
0x0000000000557d41 <+1>: mov rbp,rsp
0x0000000000557d44 <+4>: add BYTE PTR [rip+0x2f43ad],0x1 # 0x84c0f8
0x0000000000557d4b <+11>: mov rax,QWORD PTR [rip+0x2f1246] # 0x848f98
0x0000000000557d52 <+18>: cmp rbp,QWORD PTR fs:[rax]
0x0000000000557d56 <+22>: jae 0x557d5c <LLVMFuzzerTestOneInput+28>
0x0000000000557d58 <+24>: mov QWORD PTR fs:[rax],rbp
0x0000000000557d5c <+28>: call 0x557d70 <_ZL17EnvoyTestOneInputPKhm>
0x0000000000557d61 <+33>: xor eax,eax
0x0000000000557d63 <+35>: pop rbp
0x0000000000557d64 <+36>: ret
End of assembler dump.
(gdb) disass _ZL17EnvoyTestOneInputPKhm
Dump of assembler code for function _ZL17EnvoyTestOneInputPKhm:
0x0000000000557d70 <+0>: push rbp
0x0000000000557d71 <+1>: mov rbp,rsp
0x0000000000557d74 <+4>: push r14
0x0000000000557d76 <+6>: push rbx
0x0000000000557d77 <+7>: add BYTE PTR [rip+0x2f437b],0x1 # 0x84c0f9
0x0000000000557d7e <+14>: mov rbx,rsi
0x0000000000557d81 <+17>: mov r14,rdi
0x0000000000557d84 <+20>: mov rax,QWORD PTR [rip+0x2f120d] # 0x848f98
0x0000000000557d8b <+27>: cmp rbp,QWORD PTR fs:[rax]
0x0000000000557d8f <+31>: jae 0x557d95 <_ZL17EnvoyTestOneInputPKhm+37>
0x0000000000557d91 <+33>: mov QWORD PTR fs:[rax],rbp
0x0000000000557d95 <+37>: mov edi,0x498f3a94
0x0000000000557d9a <+42>: mov rsi,rbx
0x0000000000557d9d <+45>: call 0x473370 <__sanitizer_cov_trace_const_cmp8()>
0x0000000000557da2 <+50>: cmp rbx,0x498f3a94
0x0000000000557da9 <+57>: jne 0x557ddb <_ZL17EnvoyTestOneInputPKhm+107>
0x0000000000557dab <+59>: add BYTE PTR [rip+0x2f4349],0x1 # 0x84c0fb
0x0000000000557db2 <+66>: mov edi,0x11a77e0
0x0000000000557db7 <+71>: mov esi,0x5e03a0
0x0000000000557dbc <+76>: call 0x557df0
<_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc>
0x0000000000557dc1 <+81>: mov rdi,rax
0x0000000000557dc4 <+84>: mov rsi,r14
0x0000000000557dc7 <+87>: call 0x557e30
<_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKh>
0x0000000000557dcc <+92>: mov esi,0x5e03e0
0x0000000000557dd1 <+97>: mov rdi,rax
0x0000000000557dd4 <+100>: call 0x557df0
Ada Logics
London, United Kingdom
21
Envoy fuzzing improvements, April 2021
<_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc>
0x0000000000557dd9 <+105>: jmp 0x557de2 <_ZL17EnvoyTestOneInputPKhm+114>
0x0000000000557ddb <+107>: add BYTE PTR [rip+0x2f4318],0x1 # 0x84c0fa
0x0000000000557de2 <+114>: pop rbx
0x0000000000557de3 <+115>: pop r14
0x0000000000557de5 <+117>: pop rbp
0x0000000000557de6 <+118>: ret
End of assembler dump.
Configuration 2.
This configuration disables:
● Coverage and bug-finding sanitizers in many of the external libraries
● Coverage instrumentation of various folders under source/common and
source/extensions
● Coverage instrumentation of code in test/ directory.
● Coverage instrumentation of all .pb.cc files and all (.cc) files in the bazel-out directory
declare -r DI="$(
if [ "$SANITIZER" != "coverage" ]
then
# Envoy code. Disable coverage instrumentation
echo " --per_file_copt=^.*source/extensions/access_loggers/.*\.cc\$@-fsanitize-coverage=0"
echo " --per_file_copt=^.*source/extensions/filters/.*\.cc\$@-fsanitize-coverage=0"
echo " --per_file_copt=^.*source/extensions/.*\.cc\$@-fsanitize-coverage=0"
echo " --per_file_copt=^.*source/common/protobuf/.*\.cc\$@-fsanitize-coverage=0"
echo " --per_file_copt=^.*source/common/network.*\.cc\$@-fsanitize-coverage=0"
echo " --per_file_copt=^.*source/common/runtime.*\.cc\$@-fsanitize-coverage=0"
Ada Logics
London, United Kingdom
22
Envoy fuzzing improvements, April 2021
Configuration 3.
This configuration disables everything that configuration 2 disables except:
● Code under source/common and source/extensions.
declare -r DI="$(
if [ "$SANITIZER" != "coverage" ]
then
# Envoy code. Disable coverage instrumentation
Configuration 4
This configuration disables everything that configuration 3 disables and also:
● Disabling bug-finding instrumentation in files under the test/ directory.
Ada Logics
London, United Kingdom
23
Envoy fuzzing improvements, April 2021
declare -r DI="$(
if [ "$SANITIZER" != "coverage" ]
then
# Envoy code. Disable coverage instrumentation
Configuration 5
This configuration disables everything that configuration 4 disables and also:
● Disabling coverage instrumentation of all .cc files in source/server folder.
declare -r DI="$(
if [ "$SANITIZER" != "coverage" ]
then
# Envoy code. Disable coverage instrumentation
echo " --per_file_copt=^.*source/server.*\.cc\$@-fsanitize-coverage=0"
# Envoy test code. Disable coverage instrumentation
echo " --per_file_copt=^.*test/.*\.cc\$@-fsanitize-coverage=0,-fno-sanitize=all"
Ada Logics
London, United Kingdom
24
Envoy fuzzing improvements, April 2021
Configuration 6
This configuration is similar to configuration #4, however, it also includes the use of a file
with a function name regex on the path /src/blog_list.txt. This file is used by
SanitizerCoverage to disable coverage instrumentation in functions that match the regular
expression, the contents of this file is:
fun:*envoy*
declare -r DI="$(
if [ "$SANITIZER" != "coverage" ]
then
# Envoy code. Disable coverage instrumentation
Ada Logics
London, United Kingdom
25
Envoy fuzzing improvements, April 2021
Ada Logics
London, United Kingdom
26
Envoy fuzzing improvements, April 2021
Envoy::Network::Utility::readFromSockethttps://storage.googleapis.com/oss-fu
zz-coverage/envoy/reports/20210423/linux/proc/self/cwd/source/common/network/utility.cc.ht
ml#L576
Ada Logics
London, United Kingdom
27
Envoy fuzzing improvements, April 2021
Ada Logics
London, United Kingdom
28