merged RC_1_1 into master

This commit is contained in:
arvidn 2017-08-16 22:46:47 +02:00
commit 05b5b4ef4d
14 changed files with 147 additions and 60 deletions

View File

@ -15,6 +15,8 @@ Stas Khirman
Ryan Norton Ryan Norton
Andrew Resch Andrew Resch
Thanks to (github user) nervoir for bug reports
Building and maintainance of the autotools scripts: Building and maintainance of the autotools scripts:
Michael Wojciechowski Michael Wojciechowski
Peter Koeleman Peter Koeleman

View File

@ -76,6 +76,9 @@
* resume data no longer has timestamps of files * resume data no longer has timestamps of files
* require C++11 to build libtorrent * require C++11 to build libtorrent
* fix utf-8 encoding check in torrent parser
* fix infinite loop when parsing maliciously crafted torrents
* fix invalid read in parse_int in bdecoder
* fix issue with very long tracker- and web seed URLs * fix issue with very long tracker- and web seed URLs
* don't attempt to create empty files on startup, if they already exist * don't attempt to create empty files on startup, if they already exist
* fix force-recheck issue (new files would not be picked up) * fix force-recheck issue (new files would not be picked up)

View File

@ -119,6 +119,15 @@ namespace libtorrent {
TORRENT_EXTRA_EXPORT bool is_i2p_url(std::string const& url); TORRENT_EXTRA_EXPORT bool is_i2p_url(std::string const& url);
#endif #endif
// this can be used as the hash function in std::unordered_*
struct TORRENT_EXTRA_EXPORT string_hash_no_case
{ size_t operator()(std::string const& s) const; };
// these can be used as the comparison functions in std::map and std::set
struct TORRENT_EXTRA_EXPORT string_eq_no_case
{ bool operator()(std::string const& lhs, std::string const& rhs) const; };
} }
#endif #endif

View File

@ -300,7 +300,7 @@ Boolean isLegalUTF8(const UTF8 *source, int length) {
/* Everything else falls through when "true"... */ /* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) > 0xBF) return false; case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
switch (*source) { switch (*source) {
/* no fall-through in this inner switch */ /* no fall-through in this inner switch */

View File

@ -121,10 +121,12 @@ namespace {
} // anonymous namespace } // anonymous namespace
// fills in 'val' with what the string between start and the // reads the string between start and end, or up to the first occurrance of
// first occurrence of the delimiter is interpreted as an int. // 'delimiter', whichever comes first. This string is interpreted as an
// return the pointer to the delimiter, or 0 if there is a // integer which is assigned to 'val'. If there's a non-delimiter and
// parse error. val should be initialized to zero // non-digit in the range, a parse error is reported in 'ec'. If the value
// cannot be represented by the variable 'val' and overflow error is reported
// by 'ec'.
char const* parse_int(char const* start, char const* end, char delimiter char const* parse_int(char const* start, char const* end, char delimiter
, std::int64_t& val, bdecode_errors::error_code_enum& ec) , std::int64_t& val, bdecode_errors::error_code_enum& ec)
{ {
@ -150,8 +152,6 @@ namespace {
val += digit; val += digit;
++start; ++start;
} }
if (*start != delimiter)
ec = bdecode_errors::expected_colon;
return start; return start;
} }
@ -663,9 +663,11 @@ namespace {
bool negative = false; bool negative = false;
if (*ptr == '-') negative = true; if (*ptr == '-') negative = true;
bdecode_errors::error_code_enum ec = bdecode_errors::no_error; bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
parse_int(ptr + negative char const* end = parse_int(ptr + negative
, ptr + size, 'e', val, ec); , ptr + size, 'e', val, ec);
if (ec) return 0; if (ec) return 0;
TORRENT_UNUSED(end);
TORRENT_ASSERT(end < ptr + size);
if (negative) val = -val; if (negative) val = -val;
return val; return val;
} }
@ -890,6 +892,8 @@ namespace {
start = parse_int(start, end, ':', len, e); start = parse_int(start, end, ':', len, e);
if (e) if (e)
TORRENT_FAIL_BDECODE(e); TORRENT_FAIL_BDECODE(e);
if (start == end)
TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
// remaining buffer size excluding ':' // remaining buffer size excluding ':'
ptrdiff_t const buff_size = end - start - 1; ptrdiff_t const buff_size = end - start - 1;

View File

@ -131,6 +131,8 @@ namespace {
start = parse_int(start, end, ':', len, e); start = parse_int(start, end, ':', len, e);
if (e) if (e)
TORRENT_FAIL_BDECODE(e); TORRENT_FAIL_BDECODE(e);
if (start == end)
TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
// remaining buffer size excluding ':' // remaining buffer size excluding ':'
const ptrdiff_t buff_size = end - start - 1; const ptrdiff_t buff_size = end - start - 1;
@ -203,6 +205,8 @@ namespace {
start = parse_int(start, end, ':', len, e); start = parse_int(start, end, ':', len, e);
if (e) if (e)
TORRENT_FAIL_BDECODE(e); TORRENT_FAIL_BDECODE(e);
if (start == end)
TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
// remaining buffer size excluding ':' // remaining buffer size excluding ':'
const ptrdiff_t buff_size = end - start - 1; const ptrdiff_t buff_size = end - start - 1;

View File

@ -401,4 +401,27 @@ namespace libtorrent {
#endif #endif
std::size_t string_hash_no_case::operator()(std::string const& s) const
{
std::size_t ret = 5381;
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
ret = (ret * 33) ^ static_cast<std::size_t>(to_lower(*i));
return ret;
}
bool string_eq_no_case::operator()(std::string const& lhs, std::string const& rhs) const
{
if (lhs.size() != rhs.size()) return false;
std::string::const_iterator s1 = lhs.begin();
std::string::const_iterator s2 = rhs.begin();
while (s1 != lhs.end() && s2 != rhs.end())
{
if (to_lower(*s1) != to_lower(*s2)) return false;
++s1;
++s2;
}
return true;
}
} }

View File

@ -5897,6 +5897,7 @@ namespace libtorrent {
if (is_paused()) return; if (is_paused()) return;
if (m_ses.is_aborted()) return; if (m_ses.is_aborted()) return;
if (is_upload_only()) return;
// this web seed may have redirected all files to other URLs, leaving it // this web seed may have redirected all files to other URLs, leaving it
// having no file left, and there's no longer any point in connecting to // having no file left, and there's no longer any point in connecting to

View File

@ -244,8 +244,7 @@ namespace libtorrent {
continue; continue;
} }
if (code_point < 0 if (code_point < 0 || !valid_path_character(code_point))
|| !valid_path_character(code_point))
{ {
// invalid utf8 sequence, replace with "_" // invalid utf8 sequence, replace with "_"
path += '_'; path += '_';
@ -254,9 +253,14 @@ namespace libtorrent {
continue; continue;
} }
TORRENT_ASSERT(isLegalUTF8(reinterpret_cast<UTF8 const*>(element.data() + i), seq_len));
// validation passed, add it to the output string // validation passed, add it to the output string
for (std::size_t k = i; k < i + std::size_t(seq_len); ++k) for (std::size_t k = i; k < i + std::size_t(seq_len); ++k)
{
TORRENT_ASSERT(element[k] != 0);
path.push_back(element[k]); path.push_back(element[k]);
}
if (code_point == '.') ++num_dots; if (code_point == '.') ++num_dots;
@ -486,46 +490,6 @@ namespace {
return true; return true;
} }
struct string_hash_no_case
{
std::size_t operator()(std::string const& s) const
{
char const* s1 = s.c_str();
std::size_t ret = 5381;
int c;
for (;;)
{
c = *s1++;
if (c == 0)
break;
ret = (ret * 33) ^ std::size_t(to_lower(char(c)));
}
return ret;
}
};
struct string_eq_no_case
{
bool operator()(std::string const& lhs, std::string const& rhs) const
{
char c1, c2;
char const* s1 = lhs.c_str();
char const* s2 = rhs.c_str();
while (*s1 != 0 && *s2 != 0)
{
c1 = to_lower(*s1);
c2 = to_lower(*s2);
if (c1 != c2) return false;
++s1;
++s2;
}
return *s1 == *s2;
}
};
// root_dir is the name of the torrent, unless this is a single file // root_dir is the name of the torrent, unless this is a single file
// torrent, in which case it's empty. // torrent, in which case it's empty.
bool extract_files(bdecode_node const& list, file_storage& target bool extract_files(bdecode_node const& list, file_storage& target
@ -644,7 +608,7 @@ namespace {
{ {
// as long as this file already exists // as long as this file already exists
// increase the counter // increase the counter
std::uint32_t h = m_files.file_path_hash(i, empty_str); std::uint32_t const h = m_files.file_path_hash(i, empty_str);
if (!files.insert(h).second) if (!files.insert(h).second)
{ {
// This filename appears to already exist! // This filename appears to already exist!

View File

@ -62,6 +62,8 @@ namespace libtorrent {
TORRENT_ASSERT(is_outgoing()); TORRENT_ASSERT(is_outgoing());
TORRENT_ASSERT(!m_torrent.lock()->is_upload_only());
// we only want left-over bandwidth // we only want left-over bandwidth
// TODO: introduce a web-seed default class which has a low download priority // TODO: introduce a web-seed default class which has a low download priority
@ -102,9 +104,10 @@ namespace libtorrent {
// which fails because the m_num_connecting count is not consistent until // which fails because the m_num_connecting count is not consistent until
// after we call peer_connection::start // after we call peer_connection::start
m_upload_only = true; m_upload_only = true;
disconnect_if_redundant();
if (is_disconnecting()) return;
peer_connection::start(); peer_connection::start();
// disconnect_if_redundant must be called after start to keep
// m_num_connecting consistent
disconnect_if_redundant();
} }
web_connection_base::~web_connection_base() = default; web_connection_base::~web_connection_base() = default;

View File

@ -857,8 +857,9 @@ TORRENT_TEST(parse_int)
{ {
char b[] = "1234567890e"; char b[] = "1234567890e";
std::int64_t val = 0; std::int64_t val = 0;
bdecode_errors::error_code_enum ec; bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
TEST_EQUAL(ec, bdecode_errors::no_error);
TEST_EQUAL(val, 1234567890); TEST_EQUAL(val, 1234567890);
TEST_EQUAL(e, b + sizeof(b) - 2); TEST_EQUAL(e, b + sizeof(b) - 2);
} }
@ -901,7 +902,7 @@ TORRENT_TEST(parse_length_overflow)
bdecode_node e; bdecode_node e;
int ret = bdecode(b[i], b[i] + strlen(b[i]), e, ec); int ret = bdecode(b[i], b[i] + strlen(b[i]), e, ec);
TEST_EQUAL(ret, -1); TEST_EQUAL(ret, -1);
TEST_CHECK(ec == error_code(bdecode_errors::unexpected_eof)); TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
} }
} }
@ -910,9 +911,9 @@ TORRENT_TEST(expected_colon_string)
{ {
char b[] = "928"; char b[] = "928";
std::int64_t val = 0; std::int64_t val = 0;
bdecode_errors::error_code_enum ec; bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
TEST_EQUAL(ec, bdecode_errors::expected_colon); TEST_EQUAL(ec, bdecode_errors::no_error);
TEST_EQUAL(e, b + 3); TEST_EQUAL(e, b + 3);
} }
@ -1395,6 +1396,21 @@ TORRENT_TEST(partial_parse4)
TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1 ] }"); TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1 ] }");
} }
TORRENT_TEST(partial_parse_string)
{
// it's important to not have a null terminator here
// to allow address sanitizer to trigger in case the decoder reads past the
// end
char b[] = { '5', '5'};
bdecode_node e;
error_code ec;
int pos;
int ret = bdecode(b, b + sizeof(b), e, ec, &pos);
TEST_EQUAL(ret, -1);
TEST_EQUAL(pos, 2);
}
// test switch_underlying_buffer // test switch_underlying_buffer
TORRENT_TEST(switch_buffer) TORRENT_TEST(switch_buffer)
{ {

View File

@ -579,8 +579,9 @@ TORRENT_TEST(lazy_entry)
{ {
char b[] = "1234567890e"; char b[] = "1234567890e";
std::int64_t val = 0; std::int64_t val = 0;
bdecode_errors::error_code_enum ec; bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec); char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
TEST_CHECK(ec == bdecode_errors::no_error);
TEST_EQUAL(val, 1234567890); TEST_EQUAL(val, 1234567890);
TEST_EQUAL(e, b + sizeof(b) - 2); TEST_EQUAL(e, b + sizeof(b) - 2);
} }
@ -607,9 +608,9 @@ TORRENT_TEST(lazy_entry)
{ {
char b[] = "928"; char b[] = "928";
std::int64_t val = 0; std::int64_t val = 0;
bdecode_errors::error_code_enum ec; bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec); char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
TEST_CHECK(ec == bdecode_errors::expected_colon); TEST_CHECK(ec == bdecode_errors::no_error);
TEST_EQUAL(e, b + 3); TEST_EQUAL(e, b + 3);
} }

View File

@ -412,3 +412,39 @@ TORRENT_TEST(i2p_url)
TEST_CHECK(!is_i2p_url("http://i2p/foo bar")); TEST_CHECK(!is_i2p_url("http://i2p/foo bar"));
} }
#endif #endif
TORRENT_TEST(string_hash_no_case)
{
string_hash_no_case hsh;
// make sure different strings yield different hashes
TEST_CHECK(hsh("ab") != hsh("ba"));
// make sure case is ignored
TEST_EQUAL(hsh("Ab"), hsh("ab"));
TEST_EQUAL(hsh("Ab"), hsh("aB"));
// make sure zeroes in strings are supported
TEST_CHECK(hsh(std::string("\0a", 2)) != hsh(std::string("\0b", 2)));
TEST_EQUAL(hsh(std::string("\0a", 2)), hsh(std::string("\0a", 2)));
}
TORRENT_TEST(string_eq_no_case)
{
string_eq_no_case cmp;
TEST_CHECK(cmp("ab", "ba") == false);
TEST_CHECK(cmp("", ""));
TEST_CHECK(cmp("abc", "abc"));
// make sure different lengths are correctly treated as different
TEST_CHECK(cmp("abc", "ab") == false);
// make sure case is ignored
TEST_CHECK(cmp("Ab", "ab"));
TEST_CHECK(cmp("Ab", "aB"));
// make sure zeros are supported
TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0b", 2)) == false);
TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0a", 2)));
}

View File

@ -350,6 +350,10 @@ TORRENT_TEST(sanitize_path_trailing_spaces)
TORRENT_TEST(sanitize_path) TORRENT_TEST(sanitize_path)
{ {
std::string path; std::string path;
sanitize_append_path_element(path, "\0\0\xed\0\x80");
TEST_EQUAL(path, "_");
path.clear();
sanitize_append_path_element(path, "/a/"); sanitize_append_path_element(path, "/a/");
sanitize_append_path_element(path, "b"); sanitize_append_path_element(path, "b");
sanitize_append_path_element(path, "c"); sanitize_append_path_element(path, "c");
@ -530,6 +534,17 @@ TORRENT_TEST(sanitize_path)
TEST_EQUAL(path, "foobar"); TEST_EQUAL(path, "foobar");
} }
TORRENT_TEST(sanitize_path_zeroes)
{
std::string path;
sanitize_append_path_element(path, "\0foo");
TEST_EQUAL(path, "_");
path.clear();
sanitize_append_path_element(path, "\0\0\0\0");
TEST_EQUAL(path, "_");
}
TORRENT_TEST(verify_encoding) TORRENT_TEST(verify_encoding)
{ {
// verify_encoding // verify_encoding
@ -609,6 +624,12 @@ TORRENT_TEST(verify_encoding)
TEST_CHECK(!verify_encoding(test)); TEST_CHECK(!verify_encoding(test));
std::printf("%s\n", test.c_str()); std::printf("%s\n", test.c_str());
TEST_CHECK(test == "filename____"); TEST_CHECK(test == "filename____");
// missing byte header
test = "filename\xed\0\x80";
TEST_CHECK(!verify_encoding(test));
fprintf(stdout, "%s\n", test.c_str());
TEST_CHECK(test == "filename_");
} }
TORRENT_TEST(parse_torrents) TORRENT_TEST(parse_torrents)