add fuzzer for peer connection
This commit is contained in:
parent
0cf2924230
commit
5c6f027872
|
@ -65,6 +65,7 @@ fuzzer upnp ;
|
|||
fuzzer dht_node ;
|
||||
fuzzer utp ;
|
||||
fuzzer resume_data ;
|
||||
fuzzer peer_conn ;
|
||||
|
||||
local LARGE_TARGETS =
|
||||
torrent_info
|
||||
|
@ -77,6 +78,7 @@ local LARGE_TARGETS =
|
|||
file_storage_add_file
|
||||
sanitize_path
|
||||
upnp
|
||||
peer_conn
|
||||
;
|
||||
|
||||
install stage : $(TARGETS) : <install-type>EXE <location>fuzzers ;
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2019, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* Neither the name of the author 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 OWNER 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 <memory>
|
||||
#include <iostream>
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/settings_pack.hpp"
|
||||
#include "libtorrent/create_torrent.hpp"
|
||||
#include "libtorrent/torrent_info.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/random.hpp"
|
||||
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM >= 10300
|
||||
#include "libtorrent/io_context.hpp"
|
||||
#else
|
||||
#include "libtorrent/io_service.hpp"
|
||||
#endif
|
||||
|
||||
using namespace lt;
|
||||
|
||||
std::unique_ptr<session> g_ses;
|
||||
sha1_hash g_info_hash;
|
||||
int g_listen_port = 0;
|
||||
#if LIBTORRENT_VERSION_NUM >= 10300
|
||||
io_context g_ios;
|
||||
#else
|
||||
io_service g_ios;
|
||||
#endif
|
||||
|
||||
//#define DEBUG_LOGGING 1
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv)
|
||||
{
|
||||
// set up a session
|
||||
settings_pack pack;
|
||||
pack.set_int(settings_pack::piece_timeout, 1);
|
||||
pack.set_int(settings_pack::request_timeout, 1);
|
||||
pack.set_int(settings_pack::peer_timeout, 1);
|
||||
pack.set_int(settings_pack::peer_connect_timeout, 1);
|
||||
pack.set_int(settings_pack::inactivity_timeout, 1);
|
||||
pack.set_int(settings_pack::handshake_timeout, 1);
|
||||
|
||||
#ifdef DEBUG_LOGGING
|
||||
pack.set_int(settings_pack::alert_mask, 0xffffff);
|
||||
#else
|
||||
pack.set_int(settings_pack::alert_mask, alert::connect_notification
|
||||
| alert::error_notification
|
||||
| alert::status_notification
|
||||
| alert::peer_notification);
|
||||
#endif
|
||||
|
||||
pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled);
|
||||
pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled);
|
||||
|
||||
// don't waste time making outbound connections
|
||||
pack.set_bool(settings_pack::enable_outgoing_tcp, false);
|
||||
pack.set_bool(settings_pack::enable_outgoing_utp, false);
|
||||
pack.set_bool(settings_pack::enable_upnp, false);
|
||||
pack.set_bool(settings_pack::enable_natpmp, false);
|
||||
pack.set_bool(settings_pack::enable_dht, false);
|
||||
pack.set_bool(settings_pack::enable_lsd, false);
|
||||
pack.set_bool(settings_pack::enable_ip_notifier, false);
|
||||
|
||||
// pick an available listen port and only listen on loopback
|
||||
pack.set_str(settings_pack::listen_interfaces, "127.0.0.1:0");
|
||||
|
||||
g_ses = std::unique_ptr<session>(new lt::session(pack));
|
||||
|
||||
// create a torrent
|
||||
file_storage fs;
|
||||
int const piece_size = 1024 * 1024;
|
||||
std::int64_t const total_size = std::int64_t(piece_size) * 100;
|
||||
fs.add_file("test_file", total_size);
|
||||
|
||||
create_torrent t(fs, piece_size);
|
||||
|
||||
for (piece_index_t i : fs.piece_range())
|
||||
t.set_hash(i, sha1_hash("abababababababababab"));
|
||||
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), t.generate());
|
||||
auto ti = std::make_shared<torrent_info>(buf, from_span);
|
||||
|
||||
// remember the info-hash to give the fuzzer a chance to connect to it
|
||||
g_info_hash = ti->info_hash();
|
||||
|
||||
// add the torrent to the session
|
||||
add_torrent_params atp;
|
||||
atp.ti = std::move(ti);
|
||||
atp.save_path = ".";
|
||||
|
||||
g_ses->add_torrent(std::move(atp));
|
||||
|
||||
// pull the alerts for the listen socket we ended up using
|
||||
time_point const end_time = clock_type::now() + seconds(5);
|
||||
bool started = false;
|
||||
while (g_listen_port == 0 || !started)
|
||||
{
|
||||
std::vector<alert*> alerts;
|
||||
auto const now = clock_type::now();
|
||||
if (now > end_time) return -1;
|
||||
|
||||
g_ses->wait_for_alert(end_time - now);
|
||||
g_ses->pop_alerts(&alerts);
|
||||
|
||||
for (auto const a : alerts)
|
||||
{
|
||||
std::cout << a->message() << '\n';
|
||||
if (auto la = alert_cast<listen_succeeded_alert>(a))
|
||||
{
|
||||
if (la->socket_type == socket_type_t::tcp)
|
||||
{
|
||||
g_listen_port = la->port;
|
||||
std::cout << "listening on " << g_listen_port << '\n';
|
||||
}
|
||||
}
|
||||
if (alert_cast<torrent_resumed_alert>(a))
|
||||
{
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we have to destruct the session before global destructors, such as the
|
||||
// system error code category. The session objects rely on error_code during
|
||||
// its destruction
|
||||
std::atexit([]{ g_ses.reset(); });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
time_point const start_time = clock_type::now();
|
||||
#endif
|
||||
// connect
|
||||
tcp::socket s(g_ios);
|
||||
error_code ec;
|
||||
do {
|
||||
ec.clear();
|
||||
error_code ignore;
|
||||
s.connect(tcp::endpoint(make_address("127.0.0.1", ignore), g_listen_port), ec);
|
||||
} while (ec == boost::system::errc::interrupted);
|
||||
|
||||
// bittorrent handshake
|
||||
|
||||
std::vector<char> handshake(1 + 19 + 8 + 20 + 20 + size);
|
||||
std::memcpy(handshake.data(), "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04", 28);
|
||||
std::memcpy(handshake.data() + 28, g_info_hash.data(), 20);
|
||||
lt::aux::random_bytes({handshake.data() + 48, 20});
|
||||
std::memcpy(handshake.data() + 68, data, size);
|
||||
|
||||
// we're likely to fail to write entire (garbage) messages, as libtorrent may
|
||||
// disconnect us half-way through. This may fail with broken_pipe for
|
||||
// instance
|
||||
error_code ignore;
|
||||
boost::asio::write(s, boost::asio::buffer(handshake), ignore);
|
||||
|
||||
s.close();
|
||||
|
||||
// wait for the alert saying the connection was closed
|
||||
|
||||
time_point const end_time = clock_type::now() + seconds(3);
|
||||
for (;;)
|
||||
{
|
||||
std::vector<alert*> alerts;
|
||||
auto const now = clock_type::now();
|
||||
if (now > end_time) return -1;
|
||||
|
||||
g_ses->wait_for_alert(end_time - now);
|
||||
g_ses->pop_alerts(&alerts);
|
||||
|
||||
for (auto const a : alerts)
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
std::cout << duration_cast<milliseconds>(a->timestamp() - start_time).count()
|
||||
<< ": " << a->message() << '\n';
|
||||
#endif
|
||||
if (alert_cast<peer_error_alert>(a)
|
||||
|| alert_cast<peer_disconnected_alert>(a))
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
from random import shuffle
|
||||
import struct
|
||||
|
||||
corpus_dirs = [
|
||||
'torrent_info', 'upnp', 'gzip' 'base32decode', 'base32encode',
|
||||
|
@ -8,7 +10,7 @@ corpus_dirs = [
|
|||
'dht_node', 'escape_path', 'escape_string', 'file_storage_add_file', 'gzip',
|
||||
'http_parser', 'lazy_bdecode', 'parse_int', 'parse_magnet_uri', 'resume_data',
|
||||
'sanitize_path', 'torrent_info', 'upnp', 'utf8_codepoint', 'utf8_wchar', 'utp',
|
||||
'verify_encoding', 'wchar_utf8']
|
||||
'verify_encoding', 'wchar_utf8', 'peer_conn']
|
||||
|
||||
for p in corpus_dirs:
|
||||
try:
|
||||
|
@ -36,3 +38,114 @@ for x in xml_tests:
|
|||
gzip_dir = '../test'
|
||||
for f in ['zeroes.gz', 'corrupt.gz', 'invalid1.gz']:
|
||||
shutil.copy(os.path.join(gzip_dir, f), os.path.join('corpus', 'gzip'))
|
||||
|
||||
# generate peer protocol messages
|
||||
messages = []
|
||||
|
||||
|
||||
def add_length(msg):
|
||||
return struct.pack('i', len(msg)) + msg
|
||||
|
||||
|
||||
# request
|
||||
for i in range(101):
|
||||
for j in range(-1, 1):
|
||||
messages.append(add_length(struct.pack('Biii', 6, i, j, 0x4000)))
|
||||
|
||||
# cancel
|
||||
for i in range(101):
|
||||
for j in range(-1, 1):
|
||||
messages.append(add_length(struct.pack('Biii', 8, i, j, 0x4000)))
|
||||
|
||||
# piece
|
||||
for i in range(101):
|
||||
messages.append(add_length(struct.pack('Bii', 7, i, 0) + ('a' * 0x4000)))
|
||||
|
||||
# single-byte
|
||||
for i in range(256):
|
||||
messages.append(add_length(struct.pack('B', i)))
|
||||
|
||||
# reject
|
||||
for i in range(101):
|
||||
messages.append(add_length(struct.pack('Biii', 16, i, 0, 0x4000)))
|
||||
|
||||
# suggest
|
||||
for i in range(101):
|
||||
messages.append(add_length(struct.pack('Bi', 13, i)))
|
||||
|
||||
# allow-fast
|
||||
for i in range(101):
|
||||
messages.append(add_length(struct.pack('Bi', 17, i)))
|
||||
|
||||
# have
|
||||
for i in range(101):
|
||||
messages.append(add_length(struct.pack('Bi', 4, i)))
|
||||
|
||||
# DHT-port
|
||||
for i in range(101):
|
||||
messages.append(add_length(struct.pack('BH', 9, i * 10)))
|
||||
|
||||
# hash request
|
||||
for i in range(-10, 200, 20):
|
||||
for j in range(-1, 1):
|
||||
for k in range(-1, 1):
|
||||
for l in range(-1, 1):
|
||||
for m in range(-1, 1):
|
||||
messages.append(add_length(struct.pack('biiiii', 21, i, j, k, l, m)))
|
||||
|
||||
# hash reject
|
||||
for i in range(-10, 200, 20):
|
||||
for j in range(-1, 1):
|
||||
for k in range(-1, 1):
|
||||
for l in range(-1, 1):
|
||||
for m in range(-1, 1):
|
||||
messages.append(add_length(struct.pack('biiiii', 23, i, j, k, l, m)))
|
||||
|
||||
# hash
|
||||
for i in range(-10, 200, 20):
|
||||
for j in range(-1, 1):
|
||||
messages.append(add_length(struct.pack('biiiii', 22, i, j, 0, 2, 0) + ('0' * 32 * 5)))
|
||||
|
||||
# lt_dont_have
|
||||
messages.append(add_length(struct.pack('BBi', 20, 7, -1)))
|
||||
messages.append(add_length(struct.pack('BBi', 20, 7, 0)))
|
||||
messages.append(add_length(struct.pack('BBi', 20, 7, 0x7fffffff)))
|
||||
|
||||
# share mode
|
||||
messages.append(add_length(struct.pack('BBB', 20, 8, 255)))
|
||||
messages.append(add_length(struct.pack('BBB', 20, 8, 0)))
|
||||
messages.append(add_length(struct.pack('BBB', 20, 8, 1)))
|
||||
|
||||
# holepunch
|
||||
for i in range(0, 2):
|
||||
for j in range(0, 1):
|
||||
messages.append(add_length(struct.pack('BBBBiH', 20, 4, i, j, 0, 0)))
|
||||
messages.append(add_length(struct.pack('BBBBiiH', 20, 4, i, j, 0, 0, 0)))
|
||||
|
||||
# upload only
|
||||
for i in range(0, 1):
|
||||
messages.append(add_length(struct.pack('BBB', 20, 3, i)))
|
||||
|
||||
# bitfields
|
||||
bitfield_len = (100 + 7)/8
|
||||
|
||||
for i in range(256):
|
||||
messages.append(add_length(struct.pack('B', 5) + (chr(i) * bitfield_len)))
|
||||
|
||||
# extended handshake
|
||||
ext_handshake = 'd1:md11:ut_metadatai1e11:lt_donthavei2eee'
|
||||
for i in range(256):
|
||||
messages.append(add_length(struct.pack('BB', 20, 0) + ext_handshake))
|
||||
|
||||
mixes = []
|
||||
|
||||
for i in range(200):
|
||||
shuffle(messages)
|
||||
mixes.append(''.join(messages[1:20]))
|
||||
|
||||
messages += mixes
|
||||
|
||||
for m in messages:
|
||||
f = open('corpus/peer_conn/%s' % hashlib.sha1(m).hexdigest(), 'w+')
|
||||
f.write(m)
|
||||
f.close()
|
||||
|
|
Loading…
Reference in New Issue