From: Tomek Mrugalski Date: Wed, 23 Nov 2016 12:24:30 +0000 (+0100) Subject: [expriments/fuzz] Added timestamps in logs, gentle shutdown support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0145e22bec4369ea3ff27b92012e5c761503cc51;p=thirdparty%2Fkea.git [expriments/fuzz] Added timestamps in logs, gentle shutdown support --- diff --git a/doc/fuzz.txt b/doc/fuzz.txt index a1463bead5..a7da3607f7 100644 --- a/doc/fuzz.txt +++ b/doc/fuzz.txt @@ -60,8 +60,12 @@ Ubuntu 16.04 I had to do this: Set up max size of a virtual memory allowed to 4GB: ulimit -v 4096000 + You may be asked by AFL to tweak your kernel. In my case (ubuntu + 16.04), I had to tweak the scaling_governor. The instructions AFL + gives are very easy to follow. + Instruct AFL to allow 4096MB of virtual memory and run AFL: - afl-fuzz -m 4096 -i tests/fuzz-data -o fuzz-out ./kea-dhcp6 -c fuzz.json + afl-fuzz -m 4096 -i tests/fuzz-data -o fuzz-out ./kea-dhcp6 -c tests/fuzz-config/fuzz.json Here's what the switches do: -m 4096 - allow Kea to take up to 4GB memory @@ -83,3 +87,12 @@ Ubuntu 16.04 I had to do this: b) I have my fuzz.json (which is renamed doc/examples/kea6/simple.json) that tell Kea to use logging on level INFO and write output to a file. This file keeps growing. That's around 3,8MB after 20 minutes. + +8. Tweak Kea harness if needed + + There are several variables in src/bin/dhcp6/fuzz.cc that you can + tweak. By default, it will write the log to /tmp/kea-fuzz-harness.txt + every 5 packets and will terminate after 100.000 packets processed. + That mechanism is to avoid cases when Kea gets stuck and technically + running, but not processing packets. AFL should be able to restart + Kea and continue running. diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 93cb45669a..4c8851cb9e 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -417,7 +417,7 @@ bool Dhcpv6Srv::run() { // and hopefully not crash in the process. Once the packet processing // is done, Kea should let the AFL know that it's ready for the next // packet. This is done further down in this loop (see kea_fuzz_notify()). - kea_fuzz_setup(); + kea_fuzz_setup(&shutdown_); #endif /* FUZZ */ while (!shutdown_) { diff --git a/src/bin/dhcp6/fuzz.cc b/src/bin/dhcp6/fuzz.cc index 43071b138e..9e0d1cbd93 100644 --- a/src/bin/dhcp6/fuzz.cc +++ b/src/bin/dhcp6/fuzz.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -33,6 +34,18 @@ #error To use American Fuzzy Lop you have to set CC to afl-clang-fast!!! #endif +/// This is how many packets Kea will process until shutting itself down. +/// AFL should restart it. This safety switch is here for eliminating cases +/// where Kea goes into a weird state and stops processing packets properly. +const unsigned int LOOP_COUNT = 100000; + +/// This mechanism limits down the number of logs this harness prints. +/// E.g. when set to 100, it will print a message every 100 packets. +const unsigned int PRINT_EVERY = 5; + +/// This is the place where the harness log message will be printed. +const std::string PRINT_LOG("/tmp/kea-fuzz-harness.txt"); + /* * We are using pthreads directly because we might be using it with unthreaded * version of BIND, where all thread functions are mocks. Since AFL for now only @@ -45,6 +58,22 @@ static bool ready; using namespace std; +bool * shutdown_reference = NULL; + + +void kea_shutdown() { + if (shutdown_reference) { + // do we have the reference to shutdown flag from Dhcp6Srv? + // If yes, then let's set it to true. Kea will shutdown on + // its own. + *shutdown_reference = true; + } else { + // We don't have the pointer yet. Let's terminate abruptly. + exit(EXIT_SUCCESS); + } +} + + // This is the main fuzzing function. It receives data from fuzzing engine. // That data is received to stdin and then sent over the configured UDP socket. // Then it wait for a conditional, which is called in kea_fuzz_notify() from @@ -61,7 +90,7 @@ kea_main_client(void *arg) { string dst(ALL_DHCP_RELAY_AGENTS_AND_SERVERS); string port("547"); - ofstream f("/tmp/kea-fuzz-harness.txt", ios::ate); + ofstream f(PRINT_LOG.c_str(), ios::ate); const char *iface_ptr = getenv("KEA_AFL_INTERFACE"); if (iface_ptr) { @@ -79,7 +108,7 @@ kea_main_client(void *arg) { } unsigned int iface_id = if_nametoindex(iface.c_str()); - + f << "Kea AFL setup:" << endl; f << "Interface: " << iface << endl; f << "Interface index: " << iface_id << endl; @@ -108,7 +137,9 @@ kea_main_client(void *arg) { exit(EXIT_FAILURE); } - loop = 100000; + time_t t; + + loop = LOOP_COUNT; while (loop--) { ssize_t length; @@ -137,8 +168,16 @@ kea_main_client(void *arg) { ssize_t sent; - f << "Sending " << length << " bytes to " << dst << "/" << port - << " over " << iface << "/" << iface_id << endl; + t = time(0); + struct tm * now = localtime(&t); + + if (! (loop%PRINT_EVERY)) { + f << (now->tm_year + 1900) << "-" << (now->tm_mon + 1) << "-" << (now->tm_mday) + << " " << (now->tm_hour) << ":" << (now->tm_min) << ":" << (now->tm_sec) + << " Sending " << length << " bytes to " << dst << "/" << port + << " over " << iface << "/" << iface_id << ", loop iteration << " + << loop << endl; + } sent = sendto(sockfd, buf, length, 0, (struct sockaddr *) &servaddr, sizeof(servaddr)); @@ -158,12 +197,16 @@ kea_main_client(void *arg) { } } + f << LOOP_COUNT << " packets processed, terminating." << endl; + f.close(); + free(buf); close(sockfd); // @todo: shutdown kea // ns_server_flushonshutdown(ns_g_server, ISC_FALSE); // isc_app_shutdown(); + kea_shutdown(); /* * It's here just for the signature, that's how AFL detects if it's @@ -179,25 +222,26 @@ kea_main_client(void *arg) { void kea_fuzz_notify(void) { #ifdef ENABLE_AFL - /// @todo: What does this piece of code do? - /* if (getenv("AFL_CMIN")) { - ns_server_flushonshutdown(ns_g_server, ISC_FALSE); - isc_app_shutdown(); + if (getenv("AFL_CMIN")) { + kea_shutdown(); + /// @todo: What does this piece of code do? + /* ns_server_flushonshutdown(ns_g_server, ISC_FALSE); + isc_app_shutdown(); */ return; - } */ + } raise(SIGSTOP); - + if (pthread_mutex_lock(&mutex) != 0) { cout << "#### unable to lock mutex" << endl; } - + ready = true; - + if (pthread_cond_signal(&cond) != 0) { } - + if (pthread_mutex_unlock(&mutex) != 0) { cout << "Unable to unlock mutex" << endl; } @@ -205,24 +249,24 @@ kea_fuzz_notify(void) { } void -kea_fuzz_setup(void) { +kea_fuzz_setup(volatile bool* shutdown) { #ifdef ENABLE_AFL /// @todo: What are those variables? What do they do? if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) { pthread_t thread; - + if (pthread_mutex_init(&mutex, NULL) != 0) { - + } - + if (pthread_cond_init(&cond, NULL) == 0) { } - - if (pthread_create(&thread, NULL, kea_main_client, NULL) != 0) { - + + if (pthread_create(&thread, NULL, kea_main_client, (void*)shutdown) != 0) { + } } diff --git a/src/bin/dhcp6/fuzz.h b/src/bin/dhcp6/fuzz.h index dcce8b2728..98d9cdf047 100644 --- a/src/bin/dhcp6/fuzz.h +++ b/src/bin/dhcp6/fuzz.h @@ -13,7 +13,16 @@ extern "C" { void kea_fuzz_notify(void); -void kea_fuzz_setup(void); +/// @brief Sets up Kea fuzzing +/// +/// @param shutdown pointer to boolean flag that will be set to true to +/// trigger shutdown procedure +/// +/// This takes one parameter, which is a pointer to shutdown flag, +/// which should point to instance of Dhcp6Srv::shutdown_. Kea runs +/// until something sets this flag to true, which is an indication to +/// start shutdown procedure. +void kea_fuzz_setup(volatile bool * shutdown); }; diff --git a/src/bin/dhcp6/tests/fuzz-config/fuzz.json b/src/bin/dhcp6/tests/fuzz-config/fuzz.json new file mode 100644 index 0000000000..80d80b7124 --- /dev/null +++ b/src/bin/dhcp6/tests/fuzz-config/fuzz.json @@ -0,0 +1,61 @@ +# This is an example configuration file for DHCPv6 server in Kea. +# It's a basic scenario with one IPv6 subnet configured. It is +# assumed that one subnet (2001:db8:1::/64 is available directly +# over ethX interface. + +{ "Dhcp6": + +{ +# Kea is told to listen on ethX interface only. + "interfaces-config": { + "interfaces": [ "eth0" ] + }, + +# We need to specify the the database used to store leases. As of +# September 2016, four database backends are supported: MySQL, +# PostgreSQL, Cassandra, and the in-memory database, Memfile. +# We'll use memfile because it doesn't require any prior set up. + "lease-database": { + "type": "memfile" + }, + +# Addresses will be assigned with preferred and valid lifetimes +# being 3000 and 4000, respectively. Client is told to start +# renewing after 1000 seconds. If the server does not respond +# after 2000 seconds since the lease was granted, client is supposed +# to start REBIND procedure (emergency renewal that allows switching +# to a different server). + "preferred-lifetime": 3000, + "valid-lifetime": 4000, + "renew-timer": 1000, + "rebind-timer": 2000, + +# The following list defines subnets. Each subnet consists of at +# least subnet and pool entries. + "subnet6": [ + { + "pools": [ { "pool": "2001:db8:1::/80" } ], + "subnet": "2001:db8:1::/64", + "interface": "eth0" + } + ] +}, + +# The following configures logging. It assumes that messages with at least +# informational level (info, warn, error and fatal) should be logged to stdout. +"Logging": { + "loggers": [ + { + "name": "kea-dhcp6", + "output_options": [ + { + "output": "/tmp/kea-fuzz.log" + } + ], + "debuglevel": 0, + "severity": "DEBUG" + } + ] +} + +}