Skip to content

Commit 05b5b4e

Browse files
committed
merged RC_1_1 into master
2 parents bde4718 + b5fe0f9 commit 05b5b4e

14 files changed

+147
-60
lines changed

AUTHORS

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Stas Khirman
1515
Ryan Norton
1616
Andrew Resch
1717

18+
Thanks to (github user) nervoir for bug reports
19+
1820
Building and maintainance of the autotools scripts:
1921
Michael Wojciechowski
2022
Peter Koeleman

ChangeLog

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@
7676
* resume data no longer has timestamps of files
7777
* require C++11 to build libtorrent
7878

79+
* fix utf-8 encoding check in torrent parser
80+
* fix infinite loop when parsing maliciously crafted torrents
81+
* fix invalid read in parse_int in bdecoder
7982
* fix issue with very long tracker- and web seed URLs
8083
* don't attempt to create empty files on startup, if they already exist
8184
* fix force-recheck issue (new files would not be picked up)

include/libtorrent/string_util.hpp

+9
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ namespace libtorrent {
119119
TORRENT_EXTRA_EXPORT bool is_i2p_url(std::string const& url);
120120

121121
#endif
122+
123+
// this can be used as the hash function in std::unordered_*
124+
struct TORRENT_EXTRA_EXPORT string_hash_no_case
125+
{ size_t operator()(std::string const& s) const; };
126+
127+
// these can be used as the comparison functions in std::map and std::set
128+
struct TORRENT_EXTRA_EXPORT string_eq_no_case
129+
{ bool operator()(std::string const& lhs, std::string const& rhs) const; };
130+
122131
}
123132

124133
#endif

src/ConvertUTF.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ Boolean isLegalUTF8(const UTF8 *source, int length) {
300300
/* Everything else falls through when "true"... */
301301
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
302302
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
303-
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
303+
case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
304304

305305
switch (*source) {
306306
/* no fall-through in this inner switch */

src/bdecode.cpp

+11-7
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,12 @@ namespace {
121121
} // anonymous namespace
122122

123123

124-
// fills in 'val' with what the string between start and the
125-
// first occurrence of the delimiter is interpreted as an int.
126-
// return the pointer to the delimiter, or 0 if there is a
127-
// parse error. val should be initialized to zero
124+
// reads the string between start and end, or up to the first occurrance of
125+
// 'delimiter', whichever comes first. This string is interpreted as an
126+
// integer which is assigned to 'val'. If there's a non-delimiter and
127+
// non-digit in the range, a parse error is reported in 'ec'. If the value
128+
// cannot be represented by the variable 'val' and overflow error is reported
129+
// by 'ec'.
128130
char const* parse_int(char const* start, char const* end, char delimiter
129131
, std::int64_t& val, bdecode_errors::error_code_enum& ec)
130132
{
@@ -150,8 +152,6 @@ namespace {
150152
val += digit;
151153
++start;
152154
}
153-
if (*start != delimiter)
154-
ec = bdecode_errors::expected_colon;
155155
return start;
156156
}
157157

@@ -663,9 +663,11 @@ namespace {
663663
bool negative = false;
664664
if (*ptr == '-') negative = true;
665665
bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
666-
parse_int(ptr + negative
666+
char const* end = parse_int(ptr + negative
667667
, ptr + size, 'e', val, ec);
668668
if (ec) return 0;
669+
TORRENT_UNUSED(end);
670+
TORRENT_ASSERT(end < ptr + size);
669671
if (negative) val = -val;
670672
return val;
671673
}
@@ -890,6 +892,8 @@ namespace {
890892
start = parse_int(start, end, ':', len, e);
891893
if (e)
892894
TORRENT_FAIL_BDECODE(e);
895+
if (start == end)
896+
TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
893897

894898
// remaining buffer size excluding ':'
895899
ptrdiff_t const buff_size = end - start - 1;

src/lazy_bdecode.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ namespace {
131131
start = parse_int(start, end, ':', len, e);
132132
if (e)
133133
TORRENT_FAIL_BDECODE(e);
134+
if (start == end)
135+
TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
134136

135137
// remaining buffer size excluding ':'
136138
const ptrdiff_t buff_size = end - start - 1;
@@ -203,6 +205,8 @@ namespace {
203205
start = parse_int(start, end, ':', len, e);
204206
if (e)
205207
TORRENT_FAIL_BDECODE(e);
208+
if (start == end)
209+
TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
206210

207211
// remaining buffer size excluding ':'
208212
const ptrdiff_t buff_size = end - start - 1;

src/string_util.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -401,4 +401,27 @@ namespace libtorrent {
401401

402402
#endif
403403

404+
std::size_t string_hash_no_case::operator()(std::string const& s) const
405+
{
406+
std::size_t ret = 5381;
407+
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
408+
ret = (ret * 33) ^ static_cast<std::size_t>(to_lower(*i));
409+
return ret;
410+
}
411+
412+
bool string_eq_no_case::operator()(std::string const& lhs, std::string const& rhs) const
413+
{
414+
if (lhs.size() != rhs.size()) return false;
415+
416+
std::string::const_iterator s1 = lhs.begin();
417+
std::string::const_iterator s2 = rhs.begin();
418+
419+
while (s1 != lhs.end() && s2 != rhs.end())
420+
{
421+
if (to_lower(*s1) != to_lower(*s2)) return false;
422+
++s1;
423+
++s2;
424+
}
425+
return true;
426+
}
404427
}

src/torrent.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -5897,6 +5897,7 @@ namespace libtorrent {
58975897

58985898
if (is_paused()) return;
58995899
if (m_ses.is_aborted()) return;
5900+
if (is_upload_only()) return;
59005901

59015902
// this web seed may have redirected all files to other URLs, leaving it
59025903
// having no file left, and there's no longer any point in connecting to

src/torrent_info.cpp

+7-43
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ namespace libtorrent {
244244
continue;
245245
}
246246

247-
if (code_point < 0
248-
|| !valid_path_character(code_point))
247+
if (code_point < 0 || !valid_path_character(code_point))
249248
{
250249
// invalid utf8 sequence, replace with "_"
251250
path += '_';
@@ -254,9 +253,14 @@ namespace libtorrent {
254253
continue;
255254
}
256255

256+
TORRENT_ASSERT(isLegalUTF8(reinterpret_cast<UTF8 const*>(element.data() + i), seq_len));
257+
257258
// validation passed, add it to the output string
258259
for (std::size_t k = i; k < i + std::size_t(seq_len); ++k)
260+
{
261+
TORRENT_ASSERT(element[k] != 0);
259262
path.push_back(element[k]);
263+
}
260264

261265
if (code_point == '.') ++num_dots;
262266

@@ -486,46 +490,6 @@ namespace {
486490
return true;
487491
}
488492

489-
struct string_hash_no_case
490-
{
491-
std::size_t operator()(std::string const& s) const
492-
{
493-
char const* s1 = s.c_str();
494-
std::size_t ret = 5381;
495-
int c;
496-
497-
for (;;)
498-
{
499-
c = *s1++;
500-
if (c == 0)
501-
break;
502-
ret = (ret * 33) ^ std::size_t(to_lower(char(c)));
503-
}
504-
505-
return ret;
506-
}
507-
};
508-
509-
struct string_eq_no_case
510-
{
511-
bool operator()(std::string const& lhs, std::string const& rhs) const
512-
{
513-
char c1, c2;
514-
char const* s1 = lhs.c_str();
515-
char const* s2 = rhs.c_str();
516-
517-
while (*s1 != 0 && *s2 != 0)
518-
{
519-
c1 = to_lower(*s1);
520-
c2 = to_lower(*s2);
521-
if (c1 != c2) return false;
522-
++s1;
523-
++s2;
524-
}
525-
return *s1 == *s2;
526-
}
527-
};
528-
529493
// root_dir is the name of the torrent, unless this is a single file
530494
// torrent, in which case it's empty.
531495
bool extract_files(bdecode_node const& list, file_storage& target
@@ -644,7 +608,7 @@ namespace {
644608
{
645609
// as long as this file already exists
646610
// increase the counter
647-
std::uint32_t h = m_files.file_path_hash(i, empty_str);
611+
std::uint32_t const h = m_files.file_path_hash(i, empty_str);
648612
if (!files.insert(h).second)
649613
{
650614
// This filename appears to already exist!

src/web_connection_base.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ namespace libtorrent {
6262

6363
TORRENT_ASSERT(is_outgoing());
6464

65+
TORRENT_ASSERT(!m_torrent.lock()->is_upload_only());
66+
6567
// we only want left-over bandwidth
6668
// TODO: introduce a web-seed default class which has a low download priority
6769

@@ -102,9 +104,10 @@ namespace libtorrent {
102104
// which fails because the m_num_connecting count is not consistent until
103105
// after we call peer_connection::start
104106
m_upload_only = true;
105-
disconnect_if_redundant();
106-
if (is_disconnecting()) return;
107107
peer_connection::start();
108+
// disconnect_if_redundant must be called after start to keep
109+
// m_num_connecting consistent
110+
disconnect_if_redundant();
108111
}
109112

110113
web_connection_base::~web_connection_base() = default;

test/test_bdecode.cpp

+20-4
Original file line numberDiff line numberDiff line change
@@ -857,8 +857,9 @@ TORRENT_TEST(parse_int)
857857
{
858858
char b[] = "1234567890e";
859859
std::int64_t val = 0;
860-
bdecode_errors::error_code_enum ec;
860+
bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
861861
char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
862+
TEST_EQUAL(ec, bdecode_errors::no_error);
862863
TEST_EQUAL(val, 1234567890);
863864
TEST_EQUAL(e, b + sizeof(b) - 2);
864865
}
@@ -901,7 +902,7 @@ TORRENT_TEST(parse_length_overflow)
901902
bdecode_node e;
902903
int ret = bdecode(b[i], b[i] + strlen(b[i]), e, ec);
903904
TEST_EQUAL(ret, -1);
904-
TEST_CHECK(ec == error_code(bdecode_errors::unexpected_eof));
905+
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
905906
}
906907
}
907908

@@ -910,9 +911,9 @@ TORRENT_TEST(expected_colon_string)
910911
{
911912
char b[] = "928";
912913
std::int64_t val = 0;
913-
bdecode_errors::error_code_enum ec;
914+
bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
914915
char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
915-
TEST_EQUAL(ec, bdecode_errors::expected_colon);
916+
TEST_EQUAL(ec, bdecode_errors::no_error);
916917
TEST_EQUAL(e, b + 3);
917918
}
918919

@@ -1395,6 +1396,21 @@ TORRENT_TEST(partial_parse4)
13951396
TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1 ] }");
13961397
}
13971398

1399+
TORRENT_TEST(partial_parse_string)
1400+
{
1401+
// it's important to not have a null terminator here
1402+
// to allow address sanitizer to trigger in case the decoder reads past the
1403+
// end
1404+
char b[] = { '5', '5'};
1405+
1406+
bdecode_node e;
1407+
error_code ec;
1408+
int pos;
1409+
int ret = bdecode(b, b + sizeof(b), e, ec, &pos);
1410+
TEST_EQUAL(ret, -1);
1411+
TEST_EQUAL(pos, 2);
1412+
}
1413+
13981414
// test switch_underlying_buffer
13991415
TORRENT_TEST(switch_buffer)
14001416
{

test/test_bencoding.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -579,8 +579,9 @@ TORRENT_TEST(lazy_entry)
579579
{
580580
char b[] = "1234567890e";
581581
std::int64_t val = 0;
582-
bdecode_errors::error_code_enum ec;
582+
bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
583583
char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
584+
TEST_CHECK(ec == bdecode_errors::no_error);
584585
TEST_EQUAL(val, 1234567890);
585586
TEST_EQUAL(e, b + sizeof(b) - 2);
586587
}
@@ -607,9 +608,9 @@ TORRENT_TEST(lazy_entry)
607608
{
608609
char b[] = "928";
609610
std::int64_t val = 0;
610-
bdecode_errors::error_code_enum ec;
611+
bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
611612
char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
612-
TEST_CHECK(ec == bdecode_errors::expected_colon);
613+
TEST_CHECK(ec == bdecode_errors::no_error);
613614
TEST_EQUAL(e, b + 3);
614615
}
615616

test/test_string.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -412,3 +412,39 @@ TORRENT_TEST(i2p_url)
412412
TEST_CHECK(!is_i2p_url("http://i2p/foo bar"));
413413
}
414414
#endif
415+
416+
TORRENT_TEST(string_hash_no_case)
417+
{
418+
string_hash_no_case hsh;
419+
420+
// make sure different strings yield different hashes
421+
TEST_CHECK(hsh("ab") != hsh("ba"));
422+
423+
// make sure case is ignored
424+
TEST_EQUAL(hsh("Ab"), hsh("ab"));
425+
TEST_EQUAL(hsh("Ab"), hsh("aB"));
426+
427+
// make sure zeroes in strings are supported
428+
TEST_CHECK(hsh(std::string("\0a", 2)) != hsh(std::string("\0b", 2)));
429+
TEST_EQUAL(hsh(std::string("\0a", 2)), hsh(std::string("\0a", 2)));
430+
}
431+
432+
TORRENT_TEST(string_eq_no_case)
433+
{
434+
string_eq_no_case cmp;
435+
TEST_CHECK(cmp("ab", "ba") == false);
436+
TEST_CHECK(cmp("", ""));
437+
TEST_CHECK(cmp("abc", "abc"));
438+
439+
// make sure different lengths are correctly treated as different
440+
TEST_CHECK(cmp("abc", "ab") == false);
441+
442+
// make sure case is ignored
443+
TEST_CHECK(cmp("Ab", "ab"));
444+
TEST_CHECK(cmp("Ab", "aB"));
445+
446+
// make sure zeros are supported
447+
TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0b", 2)) == false);
448+
TEST_CHECK(cmp(std::string("\0a", 2), std::string("\0a", 2)));
449+
}
450+

0 commit comments

Comments
 (0)