fix handling of torrents with too large pieces

This commit is contained in:
arvidn 2019-11-24 15:08:29 +01:00 committed by Arvid Norberg
parent 4f5715afe3
commit a53d090313
6 changed files with 73 additions and 34 deletions

View File

@ -1,3 +1,4 @@
* fix handling of torrents with too large pieces
* fixed division by zero in anti-leech choker * fixed division by zero in anti-leech choker
* fixed bug in torrent_info::swap * fixed bug in torrent_info::swap

View File

@ -95,7 +95,11 @@ namespace libtorrent {
// the number of priority levels // the number of priority levels
priority_levels = 8, priority_levels = 8,
// priority factor // priority factor
prio_factor = 3 prio_factor = 3,
// max blocks per piece
// there are counters in downloading_piece that only have 15 bits to
// count blocks per piece, that's restricting this
max_blocks_per_piece = (1 << 15) - 1
}; };
struct block_info struct block_info

View File

@ -1699,6 +1699,11 @@ namespace libtorrent {
// millionths of completeness) // millionths of completeness)
std::uint32_t m_progress_ppm:20; std::uint32_t m_progress_ppm:20;
// set to true once init() completes successfully. This is important to
// track in case it fails and need to be retried if the client clears
// the torrent error
bool m_torrent_initialized:1;
#if TORRENT_USE_ASSERTS #if TORRENT_USE_ASSERTS
// set to true when torrent is start()ed. It may only be started once // set to true when torrent is start()ed. It may only be started once
bool m_was_started = false; bool m_was_started = false;

View File

@ -155,6 +155,10 @@ namespace libtorrent {
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
std::cerr << "[" << this << "] " << "piece_picker::resize()" << std::endl; std::cerr << "[" << this << "] " << "piece_picker::resize()" << std::endl;
#endif #endif
if (blocks_per_piece > max_blocks_per_piece)
throw system_error(errors::invalid_piece_size);
// allocate the piece_map to cover all pieces // allocate the piece_map to cover all pieces
// and make them invalid (as if we don't have a single piece) // and make them invalid (as if we don't have a single piece)
m_piece_map.resize(total_num_pieces, piece_pos(0, 0)); m_piece_map.resize(total_num_pieces, piece_pos(0, 0));
@ -191,9 +195,9 @@ namespace libtorrent {
m_reverse_cursor > piece_index_t(0) && (i->have() || i->filtered()); m_reverse_cursor > piece_index_t(0) && (i->have() || i->filtered());
++i, --m_reverse_cursor); ++i, --m_reverse_cursor);
m_blocks_per_piece = std::uint16_t(blocks_per_piece); m_blocks_per_piece = aux::numeric_cast<std::uint16_t>(blocks_per_piece);
m_blocks_in_last_piece = std::uint16_t(blocks_in_last_piece); m_blocks_in_last_piece = aux::numeric_cast<std::uint16_t>(blocks_in_last_piece);
if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = std::uint16_t(blocks_per_piece); if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = aux::numeric_cast<std::uint16_t>(blocks_per_piece);
TORRENT_ASSERT(m_blocks_in_last_piece <= m_blocks_per_piece); TORRENT_ASSERT(m_blocks_in_last_piece <= m_blocks_per_piece);
} }

View File

@ -226,6 +226,7 @@ bool is_downloading_state(int const st)
, m_inactive(false) , m_inactive(false)
, m_downloaded(0xffffff) , m_downloaded(0xffffff)
, m_progress_ppm(0) , m_progress_ppm(0)
, m_torrent_initialized(false)
{ {
// we cannot log in the constructor, because it relies on shared_from_this // we cannot log in the constructor, because it relies on shared_from_this
// being initialized, which happens after the constructor returns. // being initialized, which happens after the constructor returns.
@ -1776,6 +1777,15 @@ bool is_downloading_state(int const st)
return; return;
} }
int const blocks_per_piece
= (m_torrent_file->piece_length() + default_block_size - 1) / default_block_size;
if (blocks_per_piece > piece_picker::max_blocks_per_piece)
{
set_error(errors::invalid_piece_size, torrent_status::error_file_none);
pause();
return;
}
// --- MAPPED FILES --- // --- MAPPED FILES ---
file_storage const& fs = m_torrent_file->files(); file_storage const& fs = m_torrent_file->files();
if (m_add_torrent_params) if (m_add_torrent_params)
@ -1860,7 +1870,6 @@ bool is_downloading_state(int const st)
TORRENT_ASSERT((pr.start & (block_size() - 1)) == 0); TORRENT_ASSERT((pr.start & (block_size() - 1)) == 0);
int block = block_size(); int block = block_size();
int blocks_per_piece = m_torrent_file->piece_length() / block;
piece_block pb(pr.piece, pr.start / block); piece_block pb(pr.piece, pr.start / block);
for (; pr.length >= block; pr.length -= block, ++pb.block_index) for (; pr.length >= block; pr.length -= block, ++pb.block_index)
{ {
@ -1970,6 +1979,8 @@ bool is_downloading_state(int const st)
// this will remove the piece picker, if we're done with it // this will remove the piece picker, if we're done with it
maybe_done_flushing(); maybe_done_flushing();
m_torrent_initialized = true;
} }
bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const
@ -8183,7 +8194,7 @@ bool is_downloading_state(int const st)
} }
#endif #endif
// if the error happened during initialization, try again now // if the error happened during initialization, try again now
if (!m_connections_initialized && valid_metadata()) init(); if (!m_torrent_initialized && valid_metadata()) init();
if (!checking_files && should_check_files()) if (!checking_files && should_check_files())
start_checking(); start_checking();
} }

View File

@ -166,11 +166,34 @@ void test_running_torrent(std::shared_ptr<torrent_info> info, std::int64_t file_
TEST_CHECK(h.get_file_priorities() == prio); TEST_CHECK(h.get_file_priorities() == prio);
} }
void test_large_piece_size(int const size)
{
entry torrent;
entry& info = torrent["info"];
info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
info["name"] = "test";
info["piece length"] = size;
info["length"] = size;
std::vector<char> buf;
bencode(std::back_inserter(buf), torrent);
add_torrent_params atp;
atp.ti = std::make_shared<torrent_info>(buf, from_span);
atp.save_path = ".";
lt::session ses;
auto h = ses.add_torrent(atp);
TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size));
h.clear_error();
TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size));
}
} // anonymous namespace } // anonymous namespace
TORRENT_TEST(long_names) TORRENT_TEST(long_names)
{ {
entry info; entry torrent;
entry& info = torrent["info"];
info["pieces"] = "aaaaaaaaaaaaaaaaaaaa"; info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
info["name"] = "slightly shorter name, it's kind of sad that people started " info["name"] = "slightly shorter name, it's kind of sad that people started "
"the trend of incorrectly encoding the regular name field and then adding " "the trend of incorrectly encoding the regular name field and then adding "
@ -180,14 +203,17 @@ TORRENT_TEST(long_names)
"read this that particular bug should have been fixed"; "read this that particular bug should have been fixed";
info["piece length"] = 16 * 1024; info["piece length"] = 16 * 1024;
info["length"] = 3245; info["length"] = 3245;
entry torrent;
torrent["info"] = info;
std::vector<char> buf; std::vector<char> buf;
bencode(std::back_inserter(buf), torrent); bencode(std::back_inserter(buf), torrent);
error_code ec; auto ti = std::make_shared<torrent_info>(buf, from_span);
auto ti = std::make_shared<torrent_info>(buf, std::ref(ec), from_span); }
TEST_CHECK(!ec);
TORRENT_TEST(large_piece_size)
{
test_large_piece_size(32768 * 16 * 1024);
test_large_piece_size(65536 * 16 * 1024);
test_large_piece_size(65537 * 16 * 1024);
} }
TORRENT_TEST(total_wanted) TORRENT_TEST(total_wanted)
@ -202,8 +228,7 @@ TORRENT_TEST(total_wanted)
lt::create_torrent t(fs, 1024); lt::create_torrent t(fs, 1024);
std::vector<char> tmp; std::vector<char> tmp;
bencode(std::back_inserter(tmp), t.generate()); bencode(std::back_inserter(tmp), t.generate());
error_code ec; auto info = std::make_shared<torrent_info>(tmp, from_span);
auto info = std::make_shared<torrent_info>(tmp, std::ref(ec), from_span);
settings_pack pack = settings(); settings_pack pack = settings();
pack.set_int(settings_pack::alert_mask, alert::storage_notification); pack.set_int(settings_pack::alert_mask, alert::storage_notification);
@ -243,9 +268,7 @@ TORRENT_TEST(added_peers)
lt::create_torrent t(fs, 1024); lt::create_torrent t(fs, 1024);
std::vector<char> tmp; std::vector<char> tmp;
bencode(std::back_inserter(tmp), t.generate()); bencode(std::back_inserter(tmp), t.generate());
error_code ec; auto info = std::make_shared<torrent_info>(tmp, from_span);
auto info = std::make_shared<torrent_info>(
tmp, std::ref(ec), from_span);
settings_pack pack = settings(); settings_pack pack = settings();
pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130"); pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130");
@ -253,11 +276,9 @@ TORRENT_TEST(added_peers)
lt::session ses(pack); lt::session ses(pack);
add_torrent_params p = parse_magnet_uri( add_torrent_params p = parse_magnet_uri(
"magnet:?xt=urn:btih:abababababababababababababababababababab&x.pe=127.0.0.1:48081&x.pe=127.0.0.2:48082" "magnet:?xt=urn:btih:abababababababababababababababababababab&x.pe=127.0.0.1:48081&x.pe=127.0.0.2:48082");
, ec);
p.ti = info; p.ti = info;
p.save_path = "."; p.save_path = ".";
TEST_CHECK(!ec);
torrent_handle h = ses.add_torrent(std::move(p)); torrent_handle h = ses.add_torrent(std::move(p));
@ -324,8 +345,7 @@ TORRENT_TEST(torrent)
std::vector<char> tmp; std::vector<char> tmp;
std::back_insert_iterator<std::vector<char>> out(tmp); std::back_insert_iterator<std::vector<char>> out(tmp);
bencode(out, t.generate()); bencode(out, t.generate());
error_code ec; auto info = std::make_shared<torrent_info>(tmp, from_span);
auto info = std::make_shared<torrent_info>(tmp, std::ref(ec), from_span);
test_running_torrent(info, 1024); test_running_torrent(info, 1024);
} }
} }
@ -367,13 +387,12 @@ TORRENT_TEST(duplicate_is_not_error)
std::vector<char> tmp; std::vector<char> tmp;
std::back_insert_iterator<std::vector<char>> out(tmp); std::back_insert_iterator<std::vector<char>> out(tmp);
bencode(out, t.generate()); bencode(out, t.generate());
error_code ec;
int called = 0; int called = 0;
plugin_creator creator(called); plugin_creator creator(called);
add_torrent_params p; add_torrent_params p;
p.ti = std::make_shared<torrent_info>(tmp, std::ref(ec), from_span); p.ti = std::make_shared<torrent_info>(tmp, from_span);
p.flags &= ~torrent_flags::paused; p.flags &= ~torrent_flags::paused;
p.flags &= ~torrent_flags::auto_managed; p.flags &= ~torrent_flags::auto_managed;
p.flags &= ~torrent_flags::duplicate_is_error; p.flags &= ~torrent_flags::duplicate_is_error;
@ -394,13 +413,12 @@ TORRENT_TEST(duplicate_is_not_error)
TORRENT_TEST(torrent_total_size_zero) TORRENT_TEST(torrent_total_size_zero)
{ {
file_storage fs; file_storage fs;
error_code ec;
fs.add_file("test_torrent_dir2/tmp1", 0); fs.add_file("test_torrent_dir2/tmp1", 0);
TEST_CHECK(fs.num_files() == 1); TEST_CHECK(fs.num_files() == 1);
TEST_CHECK(fs.total_size() == 0); TEST_CHECK(fs.total_size() == 0);
ec.clear(); error_code ec;
lt::create_torrent t1(fs); lt::create_torrent t1(fs);
set_piece_hashes(t1, ".", ec); set_piece_hashes(t1, ".", ec);
TEST_CHECK(ec); TEST_CHECK(ec);
@ -426,8 +444,7 @@ TORRENT_TEST(rename_file)
std::vector<char> tmp; std::vector<char> tmp;
std::back_insert_iterator<std::vector<char>> out(tmp); std::back_insert_iterator<std::vector<char>> out(tmp);
bencode(out, t.generate()); bencode(out, t.generate());
error_code ec; auto info = std::make_shared<torrent_info>(tmp, from_span);
auto info = std::make_shared<torrent_info>(tmp, std::ref(ec), from_span);
TEST_EQUAL(info->files().file_path(file_index_t(0)), combine_path("test3","tmp1")); TEST_EQUAL(info->files().file_path(file_index_t(0)), combine_path("test3","tmp1"));
@ -605,8 +622,7 @@ TORRENT_TEST(queue_paused)
TORRENT_TEST(test_move_storage_no_metadata) TORRENT_TEST(test_move_storage_no_metadata)
{ {
lt::session ses(settings()); lt::session ses(settings());
error_code ec; add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab", ec);
p.save_path = "save_path"; p.save_path = "save_path";
torrent_handle h = ses.add_torrent(p); torrent_handle h = ses.add_torrent(p);
@ -620,8 +636,7 @@ TORRENT_TEST(test_move_storage_no_metadata)
TORRENT_TEST(test_have_piece_no_metadata) TORRENT_TEST(test_have_piece_no_metadata)
{ {
lt::session ses(settings()); lt::session ses(settings());
error_code ec; add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab", ec);
p.save_path = "save_path"; p.save_path = "save_path";
torrent_handle h = ses.add_torrent(p); torrent_handle h = ses.add_torrent(p);
@ -650,8 +665,7 @@ TORRENT_TEST(test_have_piece_out_of_range)
TORRENT_TEST(test_read_piece_no_metadata) TORRENT_TEST(test_read_piece_no_metadata)
{ {
lt::session ses(settings()); lt::session ses(settings());
error_code ec; add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab", ec);
p.save_path = "save_path"; p.save_path = "save_path";
torrent_handle h = ses.add_torrent(p); torrent_handle h = ses.add_torrent(p);