Merge pull request #2905

bd1f6029 http_client: rewrite header parsing manually for speed (moneromooo-monero)
ec724eb6 tests: add levin fuzz test (moneromooo-monero)
f1bdc9a4 tests: add http client fuzz test (moneromooo-monero)
0272df9e add parse_url fuzz test (moneromooo-monero)
261b0dd0 tests: add base58 fuzz test (moneromooo-monero)
fd052087 fuzz_testing: allow automatically resuming an interrupted job (moneromooo-monero)
c80bb0eb tests: don't init stuff we don't need in fuzz tests - faster (moneromooo-monero)
53b83a83 tests: better load-from-binary fuzz test data file (moneromooo-monero)
1a379ef6 fuzz_testing: build with ASAN (assumed to be available) (moneromooo-monero)
4cd4a431 fuzz_testing: create out directory if needed, and fix filename passing (moneromooo-monero)
c27d9092 Makefile: build fuzz tests statically, starts faster (moneromooo-monero)
95aa0bf7 add load_from_binary/load_from_json fuzzers (moneromooo-monero)
This commit is contained in:
Riccardo Spagni 2017-12-25 21:16:21 +02:00
commit 9d1c4408ad
No known key found for this signature in database
GPG key ID: 55432DF31CCD4FCD
20 changed files with 967 additions and 116 deletions

View file

@ -238,6 +238,16 @@ if(STATIC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZMQ_STATIC")
endif()
if(SANITIZE)
if (MSVC)
message(FATAL_ERROR "Cannot sanitize with MSVC")
else()
message(STATUS "Using ASAN")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
endif()
endif()
# Set default blockchain storage location:
# memory was the default in Cryptonote before Monero implimented LMDB, it still works but is unneccessary.
# set(DATABASE memory)

View file

@ -114,7 +114,7 @@ release-static-win32:
fuzz:
mkdir -p build/fuzz
cd build/fuzz && cmake -D BUILD_TESTS=ON -D USE_LTO=OFF -D CMAKE_C_COMPILER=afl-gcc -D CMAKE_CXX_COMPILER=afl-g++ -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=fuzz -D BUILD_TAG="linux-x64" ../.. && $(MAKE)
cd build/fuzz && cmake -D STATIC=ON -D SANITIZE=ON -D BUILD_TESTS=ON -D USE_LTO=OFF -D CMAKE_C_COMPILER=afl-gcc -D CMAKE_CXX_COMPILER=afl-g++ -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=fuzz -D BUILD_TAG="linux-x64" ../.. && $(MAKE)
clean:
@echo "WARNING: Back-up your wallet if it exists within ./build!" ; \

View file

@ -27,6 +27,7 @@
#pragma once
#include <ctype.h>
#include <boost/shared_ptr.hpp>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
@ -236,7 +237,8 @@ namespace net_utils
namespace http
{
class http_simple_client: public i_target_handler
template<typename net_client_type>
class http_simple_client_template: public i_target_handler
{
private:
enum reciev_machine_state
@ -259,7 +261,7 @@ namespace net_utils
};
blocked_mode_client m_net_client;
net_client_type m_net_client;
std::string m_host_buff;
std::string m_port;
http_client_auth m_auth;
@ -276,7 +278,7 @@ namespace net_utils
bool m_ssl;
public:
explicit http_simple_client()
explicit http_simple_client_template()
: i_target_handler()
, m_net_client()
, m_host_buff()
@ -428,6 +430,15 @@ namespace net_utils
CRITICAL_REGION_LOCAL(m_lock);
return invoke(uri, "POST", body, timeout, ppresponse_info, additional_params);
}
//---------------------------------------------------------------------------
bool test(const std::string &s, std::chrono::milliseconds timeout) // TEST FUNC ONLY
{
CRITICAL_REGION_LOCAL(m_lock);
m_net_client.set_test_data(s);
m_state = reciev_machine_state_header;
return handle_reciev(timeout);
}
//---------------------------------------------------------------------------
private:
//---------------------------------------------------------------------------
inline bool handle_reciev(std::chrono::milliseconds timeout)
@ -742,87 +753,107 @@ namespace net_utils
return true;
}
//---------------------------------------------------------------------------
inline
bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process)
inline bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process)
{
MTRACE("http_stream_filter::parse_cached_header(*)");
STATIC_REGEXP_EXPR_1(rexp_mach_field,
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)"
// 12 3 4 5 6 7 8 9 10 11
"|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
//12 13 14 15
boost::regex::icase | boost::regex::normal);
boost::smatch result;
std::string::const_iterator it_current_bound = m_cache_to_process.begin();
std::string::const_iterator it_end_bound = m_cache_to_process.end();
//lookup all fields and fill well-known fields
while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
const char *ptr = m_cache_to_process.c_str();
while (ptr[0] != '\r' || ptr[1] != '\n')
{
const size_t field_val = 14;
//const size_t field_etc_name = 11;
// optional \n
if (*ptr == '\n')
++ptr;
// an identifier composed of letters or -
const char *key_pos = ptr;
while (isalnum(*ptr) || *ptr == '_' || *ptr == '-')
++ptr;
const char *key_end = ptr;
// optional space (not in RFC, but in previous code)
if (*ptr == ' ')
++ptr;
CHECK_AND_ASSERT_MES(*ptr == ':', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process);
++ptr;
// optional whitespace, but not newlines - line folding is obsolete, let's ignore it
while (isblank(*ptr))
++ptr;
const char *value_pos = ptr;
while (*ptr != '\r' && *ptr != '\n')
++ptr;
const char *value_end = ptr;
// optional trailing whitespace
while (value_end > value_pos && isblank(*(value_end-1)))
--value_end;
if (*ptr == '\r')
++ptr;
CHECK_AND_ASSERT_MES(*ptr == '\n', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process);
++ptr;
int i = 2; //start position = 2
if(result[i++].matched)//"Connection"
body_info.m_connection = result[field_val];
else if(result[i++].matched)//"Referrer"
body_info.m_referer = result[field_val];
else if(result[i++].matched)//"Content-Length"
body_info.m_content_length = result[field_val];
else if(result[i++].matched)//"Content-Type"
body_info.m_content_type = result[field_val];
else if(result[i++].matched)//"Transfer-Encoding"
body_info.m_transfer_encoding = result[field_val];
else if(result[i++].matched)//"Content-Encoding"
body_info.m_content_encoding = result[field_val];
else if(result[i++].matched)//"Host"
{ body_info.m_host = result[field_val];
string_tools::trim(body_info.m_host);
}
else if(result[i++].matched)//"Cookie"
body_info.m_cookie = result[field_val];
else if(result[i++].matched)//"User-Agent"
body_info.m_user_agent = result[field_val];
else if(result[i++].matched)//"Origin"
body_info.m_origin = result[field_val];
else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
body_info.m_etc_fields.emplace_back(result[12], result[field_val]);
const std::string key = std::string(key_pos, key_end - key_pos);
const std::string value = std::string(value_pos, value_end - value_pos);
if (!key.empty())
{
if (!string_tools::compare_no_case(key, "Connection"))
body_info.m_connection = value;
else if(!string_tools::compare_no_case(key, "Referrer"))
body_info.m_referer = value;
else if(!string_tools::compare_no_case(key, "Content-Length"))
body_info.m_content_length = value;
else if(!string_tools::compare_no_case(key, "Content-Type"))
body_info.m_content_type = value;
else if(!string_tools::compare_no_case(key, "Transfer-Encoding"))
body_info.m_transfer_encoding = value;
else if(!string_tools::compare_no_case(key, "Content-Encoding"))
body_info.m_content_encoding = value;
else if(!string_tools::compare_no_case(key, "Host"))
body_info.m_host = value;
else if(!string_tools::compare_no_case(key, "Cookie"))
body_info.m_cookie = value;
else if(!string_tools::compare_no_case(key, "User-Agent"))
body_info.m_user_agent = value;
else if(!string_tools::compare_no_case(key, "Origin"))
body_info.m_origin = value;
else
{CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);}
it_current_bound = result[(int)result.size()-1]. first;
body_info.m_etc_fields.emplace_back(key, value);
}
}
return true;
}
inline
bool analize_first_response_line()
//---------------------------------------------------------------------------
inline bool analize_first_response_line()
{
//First line response, look like this: "HTTP/1.1 200 OK"
STATIC_REGEXP_EXPR_1(rexp_match_first_response_line, "^HTTP/(\\d+).(\\d+) ((\\d)\\d{2})( [^\n]*)?\r?\n", boost::regex::icase | boost::regex::normal);
// 1 2 34 5
//size_t match_len = 0;
boost::smatch result;
if(boost::regex_search( m_header_cache, result, rexp_match_first_response_line, boost::match_default) && result[0].matched)
{
CHECK_AND_ASSERT_MES(result[1].matched&&result[2].matched, false, "http_stream_filter::handle_invoke_reply_line() assert failed...");
m_response_info.m_http_ver_hi = boost::lexical_cast<int>(result[1]);
m_response_info.m_http_ver_lo = boost::lexical_cast<int>(result[2]);
m_response_info.m_response_code = boost::lexical_cast<int>(result[3]);
const char *ptr = m_header_cache.c_str();
CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line: " + m_header_cache);
ptr += 5;
CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache);
unsigned long ul;
char *end;
ul = strtoul(ptr, &end, 10);
CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line: " + m_header_cache);
m_response_info.m_http_ver_hi = ul;
ptr = end + 1;
CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr);
ul = strtoul(ptr, &end, 10);
CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr);
m_response_info.m_http_ver_lo = ul;
ptr = end + 1;
while (isblank(*ptr))
++ptr;
CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache);
ul = strtoul(ptr, &end, 10);
CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line: " + m_header_cache);
m_response_info.m_response_code = ul;
ptr = end;
// ignore the optional text, till the end
while (*ptr != '\r' && *ptr != '\n')
++ptr;
if (*ptr == '\r')
++ptr;
CHECK_AND_ASSERT_MES(*ptr == '\n', false, "Invalid first response line: " << m_header_cache);
++ptr;
m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second));
m_header_cache.erase(0, ptr - m_header_cache.c_str());
return true;
}else
{
LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache);
return false;
}
}
inline
bool set_reply_content_encoder()
@ -957,6 +988,7 @@ namespace net_utils
return true;
}
};
typedef http_simple_client_template<blocked_mode_client> http_simple_client;
}
}
}

View file

@ -10,12 +10,20 @@ fi
type="$1"
if test -z "$type"
then
echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction"
echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin"
exit 1
fi
case "$type" in
block|transaction|signature|cold-outputs|cold-transaction) ;;
*) echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction"; exit 1 ;;
block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin) ;;
*) echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin"; exit 1 ;;
esac
afl-fuzz -i tests/data/fuzz/$type -m 150 -t 250 -o fuzz-out/$type build/fuzz/tests/fuzz/${type}_fuzz_tests
if test -d "fuzz-out/$type"
then
dir="-"
else
dir="tests/data/fuzz/$type"
fi
mkdir -p fuzz-out
afl-fuzz -i "$dir" -m none -t 250 -o fuzz-out/$type build/fuzz/tests/fuzz/${type}_fuzz_tests @@

View file

View file

@ -0,0 +1 @@
9zZBkWRgMNPEnVofRFqWK9MKBwgXNyKELBJSttxb1t2UhDM114URntt5iYcXzXusoHZygfSojsbviXZhnP9pJ4p2SDcv81L

View file

@ -0,0 +1,8 @@
HTTP/1.1 200 Ok
Server: Epee-based
Content-Length: 5
Content-Type: text/plain
Last-Modified: Mon, 11 Dec 2017 09:03:03 GMT
Accept-Ranges: bytes
foo

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1 @@
127.0.0.1

View file

@ -0,0 +1 @@
iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=

View file

@ -89,3 +89,80 @@ set_property(TARGET cold-transaction_fuzz_tests
PROPERTY
FOLDER "tests")
add_executable(load-from-binary_fuzz_tests load_from_binary.cpp fuzzer.cpp)
target_link_libraries(load-from-binary_fuzz_tests
PRIVATE
common
epee
${Boost_PROGRAM_OPTIONS_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET load-from-binary_fuzz_tests
PROPERTY
FOLDER "tests")
add_executable(load-from-json_fuzz_tests load_from_json.cpp fuzzer.cpp)
target_link_libraries(load-from-json_fuzz_tests
PRIVATE
common
epee
${Boost_PROGRAM_OPTIONS_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET load-from-json_fuzz_tests
PROPERTY
FOLDER "tests")
add_executable(base58_fuzz_tests base58.cpp fuzzer.cpp)
target_link_libraries(base58_fuzz_tests
PRIVATE
common
epee
${Boost_PROGRAM_OPTIONS_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET base58_fuzz_tests
PROPERTY
FOLDER "tests")
add_executable(parse-url_fuzz_tests parse_url.cpp fuzzer.cpp)
target_link_libraries(parse-url_fuzz_tests
PRIVATE
epee
${Boost_REGEX_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET parse-url_fuzz_tests
PROPERTY
FOLDER "tests")
add_executable(http-client_fuzz_tests http-client.cpp fuzzer.cpp)
target_link_libraries(http-client_fuzz_tests
PRIVATE
epee
${Boost_THREAD_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET http-client_fuzz_tests
PROPERTY
FOLDER "tests")
add_executable(levin_fuzz_tests levin.cpp fuzzer.cpp)
target_link_libraries(levin_fuzz_tests
PRIVATE
common
epee
${Boost_THREAD_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET levin_fuzz_tests
PROPERTY
FOLDER "tests")

74
tests/fuzz/base58.cpp Normal file
View file

@ -0,0 +1,74 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "include_base_utils.h"
#include "file_io_utils.h"
#include "common/base58.h"
#include "fuzzer.h"
class Base58Fuzzer: public Fuzzer
{
public:
Base58Fuzzer() {}
virtual int init();
virtual int run(const std::string &filename);
};
int Base58Fuzzer::init()
{
return 0;
}
int Base58Fuzzer::run(const std::string &filename)
{
std::string s;
if (!epee::file_io_utils::load_file_to_string(filename, s))
{
std::cout << "Error: failed to load file " << filename << std::endl;
return 1;
}
try
{
std::string data;
tools::base58::decode(s, data);
}
catch (const std::exception &e)
{
std::cerr << "Failed to load from binary: " << e.what() << std::endl;
return 1;
}
return 0;
}
int main(int argc, const char **argv)
{
Base58Fuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer);
}

View file

@ -44,41 +44,11 @@ static int __AFL_LOOP(int)
}
#endif
using namespace epee;
using namespace boost::program_options;
int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer)
{
TRY_ENTRY();
tools::on_startup();
string_tools::set_module_name_and_folder(argv[0]);
//set up logging options
mlog_configure(mlog_get_default_log_path("fuzztests.log"), true);
mlog_set_log("*:FATAL,logging:none");
options_description desc_options("Allowed options");
command_line::add_arg(desc_options, command_line::arg_help);
variables_map vm;
bool r = command_line::handle_error_helper(desc_options, [&]()
{
store(parse_command_line(argc, argv, desc_options), vm);
notify(vm);
return true;
});
if (!r)
return 1;
if (command_line::get_arg(vm, command_line::arg_help))
{
std::cout << desc_options << std::endl;
return 0;
}
if (argc < 2)
{
std::cout << desc_options << std::endl;
std::cout << "usage: " << argv[0] << " " << "<filename>" << std::endl;
return 1;
}
@ -94,6 +64,5 @@ int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer)
return ret;
}
CATCH_ENTRY_L0("fuzzer_main", 1);
return 0;
}

View file

@ -0,0 +1,98 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "include_base_utils.h"
#include "file_io_utils.h"
#include "net/http_client.h"
#include "fuzzer.h"
class dummy_client
{
public:
bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") { return true; }
bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") { return true; }
bool disconnect() { return true; }
bool send(const std::string& buff, std::chrono::milliseconds timeout) { return true; }
bool send(const void* data, size_t sz) { return true; }
bool is_connected() { return true; }
bool recv(std::string& buff, std::chrono::milliseconds timeout)
{
buff = data;
data.clear();
return true;
}
void set_test_data(const std::string &s) { data = s; }
private:
std::string data;
};
class HTTPClientFuzzer: public Fuzzer
{
public:
HTTPClientFuzzer() {}
virtual int init();
virtual int run(const std::string &filename);
private:
epee::net_utils::http::http_simple_client_template<dummy_client> client;
};
int HTTPClientFuzzer::init()
{
return 0;
}
int HTTPClientFuzzer::run(const std::string &filename)
{
std::string s;
if (!epee::file_io_utils::load_file_to_string(filename, s))
{
std::cout << "Error: failed to load file " << filename << std::endl;
return 1;
}
try
{
client.test(s, std::chrono::milliseconds(1000));
}
catch (const std::exception &e)
{
std::cerr << "Failed to test http client: " << e.what() << std::endl;
return 1;
}
return 0;
}
int main(int argc, const char **argv)
{
HTTPClientFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer);
}

345
tests/fuzz/levin.cpp Normal file
View file

@ -0,0 +1,345 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "include_base_utils.h"
#include "file_io_utils.h"
#include "net/net_utils_base.h"
#include "net/abstract_tcp_server2.h"
#include "storages/levin_abstract_invoke2.h"
#include "net/levin_protocol_handler_async.h"
#include "fuzzer.h"
namespace
{
class call_counter
{
public:
call_counter() : m_counter(0) { }
// memory_order_relaxed is enough for call counter
void inc() volatile { m_counter.fetch_add(1, std::memory_order_relaxed); }
size_t get() volatile const { return m_counter.load(std::memory_order_relaxed); }
void reset() volatile { m_counter.store(0, std::memory_order_relaxed); }
private:
std::atomic<size_t> m_counter;
};
struct test_levin_connection_context : public epee::net_utils::connection_context_base
{
};
typedef epee::levin::async_protocol_handler_config<test_levin_connection_context> test_levin_protocol_handler_config;
typedef epee::levin::async_protocol_handler<test_levin_connection_context> test_levin_protocol_handler;
struct test_levin_commands_handler : public epee::levin::levin_commands_handler<test_levin_connection_context>
{
test_levin_commands_handler()
: m_return_code(LEVIN_OK)
, m_last_command(-1)
{
}
virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, test_levin_connection_context& context)
{
m_invoke_counter.inc();
boost::unique_lock<boost::mutex> lock(m_mutex);
m_last_command = command;
m_last_in_buf = in_buff;
buff_out = m_invoke_out_buf;
return m_return_code;
}
virtual int notify(int command, const std::string& in_buff, test_levin_connection_context& context)
{
m_notify_counter.inc();
boost::unique_lock<boost::mutex> lock(m_mutex);
m_last_command = command;
m_last_in_buf = in_buff;
return m_return_code;
}
virtual void callback(test_levin_connection_context& context)
{
m_callback_counter.inc();
//std::cout << "test_levin_commands_handler::callback()" << std::endl;
}
virtual void on_connection_new(test_levin_connection_context& context)
{
m_new_connection_counter.inc();
//std::cout << "test_levin_commands_handler::on_connection_new()" << std::endl;
}
virtual void on_connection_close(test_levin_connection_context& context)
{
m_close_connection_counter.inc();
//std::cout << "test_levin_commands_handler::on_connection_close()" << std::endl;
}
size_t invoke_counter() const { return m_invoke_counter.get(); }
size_t notify_counter() const { return m_notify_counter.get(); }
size_t callback_counter() const { return m_callback_counter.get(); }
size_t new_connection_counter() const { return m_new_connection_counter.get(); }
size_t close_connection_counter() const { return m_close_connection_counter.get(); }
int return_code() const { return m_return_code; }
void return_code(int v) { m_return_code = v; }
const std::string& invoke_out_buf() const { return m_invoke_out_buf; }
void invoke_out_buf(const std::string& v) { m_invoke_out_buf = v; }
int last_command() const { return m_last_command; }
const std::string& last_in_buf() const { return m_last_in_buf; }
private:
call_counter m_invoke_counter;
call_counter m_notify_counter;
call_counter m_callback_counter;
call_counter m_new_connection_counter;
call_counter m_close_connection_counter;
boost::mutex m_mutex;
int m_return_code;
std::string m_invoke_out_buf;
int m_last_command;
std::string m_last_in_buf;
};
class test_connection : public epee::net_utils::i_service_endpoint
{
public:
test_connection(boost::asio::io_service& io_service, test_levin_protocol_handler_config& protocol_config)
: m_io_service(io_service)
, m_protocol_handler(this, protocol_config, m_context)
, m_send_return(true)
{
}
void start()
{
m_protocol_handler.after_init_connection();
}
// Implement epee::net_utils::i_service_endpoint interface
virtual bool do_send(const void* ptr, size_t cb)
{
m_send_counter.inc();
boost::unique_lock<boost::mutex> lock(m_mutex);
m_last_send_data.append(reinterpret_cast<const char*>(ptr), cb);
return m_send_return;
}
virtual bool close() { return true; }
virtual bool call_run_once_service_io() { return true; }
virtual bool request_callback() { return true; }
virtual boost::asio::io_service& get_io_service() { return m_io_service; }
virtual bool add_ref() { return true; }
virtual bool release() { return true; }
size_t send_counter() const { return m_send_counter.get(); }
const std::string& last_send_data() const { return m_last_send_data; }
void reset_last_send_data() { boost::unique_lock<boost::mutex> lock(m_mutex); m_last_send_data.clear(); }
bool send_return() const { return m_send_return; }
void send_return(bool v) { m_send_return = v; }
public:
test_levin_connection_context m_context;
test_levin_protocol_handler m_protocol_handler;
private:
boost::asio::io_service& m_io_service;
call_counter m_send_counter;
boost::mutex m_mutex;
std::string m_last_send_data;
bool m_send_return;
};
#if 0
class async_protocol_handler_test : public ::testing::Test
{
public:
const static uint64_t invoke_timeout = 5 * 1000;
const static size_t max_packet_size = 10 * 1024 * 1024;
typedef std::unique_ptr<test_connection> test_connection_ptr;
async_protocol_handler_test():
m_pcommands_handler(new test_levin_commands_handler()),
m_commands_handler(*m_pcommands_handler)
{
m_handler_config.set_handler(m_pcommands_handler, [](epee::levin::levin_commands_handler<test_levin_connection_context> *handler) { delete handler; });
m_handler_config.m_invoke_timeout = invoke_timeout;
m_handler_config.m_max_packet_size = max_packet_size;
}
virtual void SetUp()
{
}
protected:
test_connection_ptr create_connection(bool start = true)
{
test_connection_ptr conn(new test_connection(m_io_service, m_handler_config));
if (start)
{
conn->start();
}
return conn;
}
protected:
boost::asio::io_service m_io_service;
test_levin_protocol_handler_config m_handler_config;
test_levin_commands_handler *m_pcommands_handler, &m_commands_handler;
};
class positive_test_connection_to_levin_protocol_handler_calls : public async_protocol_handler_test
{
};
class test_levin_protocol_handler__hanle_recv_with_invalid_data : public async_protocol_handler_test
{
public:
static const int expected_command = 5615871;
static const int expected_return_code = 782546;
test_levin_protocol_handler__hanle_recv_with_invalid_data()
: m_expected_invoke_out_buf(512, 'y')
{
}
virtual void SetUp()
{
async_protocol_handler_test::SetUp();
m_conn = create_connection();
m_in_data.assign(256, 't');
m_req_head.m_signature = LEVIN_SIGNATURE;
m_req_head.m_cb = m_in_data.size();
m_req_head.m_have_to_return_data = true;
m_req_head.m_command = expected_command;
m_req_head.m_return_code = LEVIN_OK;
m_req_head.m_flags = LEVIN_PACKET_REQUEST;
m_req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
m_commands_handler.return_code(expected_return_code);
m_commands_handler.invoke_out_buf(m_expected_invoke_out_buf);
}
protected:
void prepare_buf()
{
m_buf.assign(reinterpret_cast<const char*>(&m_req_head), sizeof(m_req_head));
m_buf += m_in_data;
}
protected:
test_connection_ptr m_conn;
epee::levin::bucket_head2 m_req_head;
std::string m_in_data;
std::string m_buf;
std::string m_expected_invoke_out_buf;
};
#endif
}
class LevinFuzzer: public Fuzzer
{
public:
LevinFuzzer() {} //: handler(endpoint, config, context) {}
virtual int init();
virtual int run(const std::string &filename);
private:
//epee::net_utils::connection_context_base context;
//epee::levin::async_protocol_handler<> handler;
};
int LevinFuzzer::init()
{
return 0;
}
int LevinFuzzer::run(const std::string &filename)
{
std::string s;
//
epee::levin::bucket_head2 req_head;
req_head.m_signature = LEVIN_SIGNATURE;
req_head.m_cb = 0;
req_head.m_have_to_return_data = true;
req_head.m_command = 2000;
req_head.m_flags = LEVIN_PACKET_REQUEST;
req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
FILE *f=fopen("/tmp/out.levin", "w");
fwrite(&req_head,sizeof(req_head),1, f);
fclose(f);
//
if (!epee::file_io_utils::load_file_to_string(filename, s))
{
std::cout << "Error: failed to load file " << filename << std::endl;
return 1;
}
try
{
//std::unique_ptr<test_connection> conn = new test();
boost::asio::io_service io_service;
test_levin_protocol_handler_config m_handler_config;
test_levin_commands_handler *m_pcommands_handler = new test_levin_commands_handler();
m_handler_config.set_handler(m_pcommands_handler, [](epee::levin::levin_commands_handler<test_levin_connection_context> *handler) { delete handler; });
std::unique_ptr<test_connection> conn(new test_connection(io_service, m_handler_config));
conn->start();
//m_commands_handler.invoke_out_buf(expected_out_data);
//m_commands_handler.return_code(expected_return_code);
conn->m_protocol_handler.handle_recv(s.data(), s.size());
}
catch (const std::exception &e)
{
std::cerr << "Failed to test http client: " << e.what() << std::endl;
return 1;
}
return 0;
}
int main(int argc, const char **argv)
{
LevinFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer);
}

View file

@ -0,0 +1,76 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "include_base_utils.h"
#include "file_io_utils.h"
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage_template_helper.h"
#include "storages/portable_storage_base.h"
#include "fuzzer.h"
class PortableStorageFuzzer: public Fuzzer
{
public:
PortableStorageFuzzer() {}
virtual int init();
virtual int run(const std::string &filename);
};
int PortableStorageFuzzer::init()
{
return 0;
}
int PortableStorageFuzzer::run(const std::string &filename)
{
std::string s;
if (!epee::file_io_utils::load_file_to_string(filename, s))
{
std::cout << "Error: failed to load file " << filename << std::endl;
return 1;
}
try
{
epee::serialization::portable_storage ps;
ps.load_from_binary(s);
}
catch (const std::exception &e)
{
std::cerr << "Failed to load from binary: " << e.what() << std::endl;
return 1;
}
return 0;
}
int main(int argc, const char **argv)
{
PortableStorageFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer);
}

View file

@ -0,0 +1,76 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "include_base_utils.h"
#include "file_io_utils.h"
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage_template_helper.h"
#include "storages/portable_storage_base.h"
#include "fuzzer.h"
class PortableStorageFuzzer: public Fuzzer
{
public:
PortableStorageFuzzer() {}
virtual int init();
virtual int run(const std::string &filename);
};
int PortableStorageFuzzer::init()
{
return 0;
}
int PortableStorageFuzzer::run(const std::string &filename)
{
std::string s;
if (!epee::file_io_utils::load_file_to_string(filename, s))
{
std::cout << "Error: failed to load file " << filename << std::endl;
return 1;
}
try
{
epee::serialization::portable_storage ps;
ps.load_from_json(s);
}
catch (const std::exception &e)
{
std::cerr << "Failed to load from binary: " << e.what() << std::endl;
return 1;
}
return 0;
}
int main(int argc, const char **argv)
{
PortableStorageFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer);
}

74
tests/fuzz/parse_url.cpp Normal file
View file

@ -0,0 +1,74 @@
// Copyright (c) 2017, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "include_base_utils.h"
#include "file_io_utils.h"
#include "net/net_parse_helpers.h"
#include "fuzzer.h"
class ParseURLFuzzer: public Fuzzer
{
public:
ParseURLFuzzer() {}
virtual int init();
virtual int run(const std::string &filename);
};
int ParseURLFuzzer::init()
{
return 0;
}
int ParseURLFuzzer::run(const std::string &filename)
{
std::string s;
if (!epee::file_io_utils::load_file_to_string(filename, s))
{
std::cout << "Error: failed to load file " << filename << std::endl;
return 1;
}
try
{
epee::net_utils::http::url_content url;
epee::net_utils::parse_url(s, url);
}
catch (const std::exception &e)
{
std::cerr << "Failed to load from binary: " << e.what() << std::endl;
return 1;
}
return 0;
}
int main(int argc, const char **argv)
{
ParseURLFuzzer fuzzer;
return run_fuzzer(argc, argv, fuzzer);
}