@@ -78,6 +78,14 @@ namespace libtorrent {
78
78
79
79
namespace {
80
80
81
+ // this is an arbitrary limit to avoid malicious torrents causing
82
+ // unreasaonably large allocations for the merkle hash tree
83
+ // the size of the tree would be max_pieces * sizeof(int) * 2
84
+ // which is about 8 MB with this limit
85
+ // TODO: remove this limit and the overloads that imply it, in favour of
86
+ // using load_torrent_limits
87
+ constexpr int default_piece_limit = 0x100000 ;
88
+
81
89
bool valid_path_character (std::int32_t const c)
82
90
{
83
91
#ifdef TORRENT_WINDOWS
@@ -539,16 +547,21 @@ namespace {
539
547
}
540
548
541
549
int load_file (std::string const & filename, std::vector<char >& v
542
- , error_code& ec)
550
+ , error_code& ec, int const max_buffer_size = 80000000 )
543
551
{
544
552
ec.clear ();
545
553
file f;
546
554
if (!f.open (filename, open_mode::read_only, ec)) return -1 ;
547
- std::int64_t s = f.get_size (ec);
555
+ std::int64_t const s = f.get_size (ec);
548
556
if (ec) return -1 ;
557
+ if (s > max_buffer_size)
558
+ {
559
+ ec = errors::metadata_too_large;
560
+ return -1 ;
561
+ }
549
562
v.resize (std::size_t (s));
550
563
if (s == 0 ) return 0 ;
551
- std::int64_t read = f.readv (0 , {v}, ec);
564
+ std::int64_t const read = f.readv (0 , {v}, ec);
552
565
if (read != s) return -3 ;
553
566
if (ec) return -3 ;
554
567
return 0 ;
@@ -869,6 +882,48 @@ namespace {
869
882
INVARIANT_CHECK;
870
883
}
871
884
885
+ torrent_info::torrent_info (bdecode_node const & torrent_file
886
+ , load_torrent_limits const & cfg)
887
+ {
888
+ error_code ec;
889
+ if (!parse_torrent_file (torrent_file, ec, cfg.max_pieces ))
890
+ aux::throw_ex<system_error>(ec);
891
+
892
+ INVARIANT_CHECK;
893
+ }
894
+
895
+ torrent_info::torrent_info (span<char const > buffer
896
+ , load_torrent_limits const & cfg, from_span_t )
897
+ {
898
+ error_code ec;
899
+ bdecode_node e = bdecode (buffer, ec, nullptr
900
+ , cfg.max_decode_depth , cfg.max_decode_tokens );
901
+ if (ec) aux::throw_ex<system_error>(ec);
902
+
903
+ if (!parse_torrent_file (e, ec, cfg.max_pieces ))
904
+ aux::throw_ex<system_error>(ec);
905
+
906
+ INVARIANT_CHECK;
907
+ }
908
+
909
+ torrent_info::torrent_info (std::string const & filename
910
+ , load_torrent_limits const & cfg)
911
+ {
912
+ std::vector<char > buf;
913
+ error_code ec;
914
+ int ret = load_file (filename, buf, ec, cfg.max_buffer_size );
915
+ if (ret < 0 ) aux::throw_ex<system_error>(ec);
916
+
917
+ bdecode_node e = bdecode (buf, ec, nullptr , cfg.max_decode_depth
918
+ , cfg.max_decode_tokens );
919
+ if (ec) aux::throw_ex<system_error>(ec);
920
+
921
+ if (!parse_torrent_file (e, ec, cfg.max_pieces ))
922
+ aux::throw_ex<system_error>(ec);
923
+
924
+ INVARIANT_CHECK;
925
+ }
926
+
872
927
#if TORRENT_ABI_VERSION == 1
873
928
torrent_info::torrent_info (std::wstring const & filename)
874
929
{
@@ -1004,8 +1059,13 @@ namespace {
1004
1059
return m_info_dict.dict_find_string_value (" ssl-cert" );
1005
1060
}
1006
1061
1062
+ bool torrent_info::parse_info_section (bdecode_node const & e, error_code& ec)
1063
+ {
1064
+ return parse_info_section (e, ec, default_piece_limit);
1065
+ }
1066
+
1007
1067
bool torrent_info::parse_info_section (bdecode_node const & info
1008
- , error_code& ec)
1068
+ , error_code& ec, int const max_pieces )
1009
1069
{
1010
1070
if (info.type () != bdecode_node::dict_t )
1011
1071
{
@@ -1129,12 +1189,6 @@ namespace {
1129
1189
return false ;
1130
1190
}
1131
1191
1132
- // this is an arbitrary limit to avoid malicious torrents causing
1133
- // unreasaonably large allocations for the merkle hash tree
1134
- // the size of the tree would be max_pieces * sizeof(int) * 2
1135
- // which is about 6.3 MB with this limit
1136
- const int max_pieces = 0xC0000 ;
1137
-
1138
1192
// we expect the piece hashes to be < 2 GB in size
1139
1193
if (files.num_pieces () >= std::numeric_limits<int >::max () / 20
1140
1194
|| files.num_pieces () > max_pieces)
@@ -1315,6 +1369,12 @@ namespace {
1315
1369
1316
1370
bool torrent_info::parse_torrent_file (bdecode_node const & torrent_file
1317
1371
, error_code& ec)
1372
+ {
1373
+ return parse_torrent_file (torrent_file, ec, default_piece_limit);
1374
+ }
1375
+
1376
+ bool torrent_info::parse_torrent_file (bdecode_node const & torrent_file
1377
+ , error_code& ec, int const piece_limit)
1318
1378
{
1319
1379
if (torrent_file.type () != bdecode_node::dict_t )
1320
1380
{
@@ -1342,7 +1402,7 @@ namespace {
1342
1402
ec = errors::torrent_missing_info;
1343
1403
return false ;
1344
1404
}
1345
- if (!parse_info_section (info, ec)) return false ;
1405
+ if (!parse_info_section (info, ec, piece_limit )) return false ;
1346
1406
resolve_duplicate_filenames ();
1347
1407
1348
1408
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
0 commit comments