CVE-2014-8509 (published 2014-10-31T14:55Z)

The lazy_bdecode function in BitTorrent bootstrap-dht (aka Bootstrap) allows remote attackers to execute arbitrary code via a crafted packet, which triggers an out-of-bounds read, related to "Improper Indexing."

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/bittorrent/bootstrap-dht
commit_details: repo_url: https://github.com/bittorrent/bootstrap-dht
author: arvidn
author_date: 2014-10-23 16:23:01-07:00
msg: update version of lazy_bdecode from libtorrent
repository: repo_url: https://github.com/bittorrent/bootstrap-dht
repo_name: bittorrent/bootstrap-dht
description: DHT bootstrap server
repo_language: C++
owner: bittorrent
file_changes: filename: lazy_bdecode.cpp
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 /*
 
-Copyright (c) 2008-2012, Arvid Norberg
+Copyright (c) 2008-2014, Arvid Norberg
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -45,35 +45,62 @@ namespace
 namespace libtorrent
 {
 
-#define TORRENT_FAIL_BDECODE(code) \
-	{ \
-		ec = make_error_code(code); \
-		while (!stack.empty()) { \
-			top = stack.back(); \
-			if (top->type() == lazy_entry::dict_t || top->type() == lazy_entry::list_t) top->pop(); \
-			stack.pop_back(); \
-		} \
-		if (error_pos) *error_pos = start - orig_start; \
-		return -1; \
+	namespace
+	{
+		int fail(int* error_pos
+			, std::vector<lazy_entry*>& stack
+			, char const* start
+			, char const* orig_start)
+		{
+			while (!stack.empty()) {
+				lazy_entry* top = stack.back();
+				if (top->type() == lazy_entry::dict_t || top->type() == lazy_entry::list_t)
+				{
+					top->pop();
+					break;
+				}
+				stack.pop_back();
+			}
+			if (error_pos) *error_pos = start - orig_start;
+			return -1;
+		}
 	}
 
-	bool is_digit(char c) { return c >= '0' && c <= '9'; }
+#define TORRENT_FAIL_BDECODE(code) do { ec = make_error_code(code); return fail(error_pos, stack, start, orig_start); } while (false)
 
-	bool is_print(char c) { return c >= 32 && c < 127; }
+	namespace { bool numeric(char c) { return c >= '0' && c <= '9'; } }
 
 	// fills in 'val' with what the string between start and the
 	// first occurance of the delimiter is interpreted as an int.
 	// return the pointer to the delimiter, or 0 if there is a
 	// parse error. val should be initialized to zero
-	char const* parse_int(char const* start, char const* end, char delimiter, boost::int64_t& val)
+	char const* parse_int(char const* start, char const* end, char delimiter
+		, boost::int64_t& val, bdecode_errors::error_code_enum& ec)
 	{
 		while (start < end && *start != delimiter)
 		{
-			if (!is_digit(*start)) { return 0; }
+			if (!numeric(*start))
+			{
+				ec = bdecode_errors::expected_string;
+				return start;
+			}
+			if (val > INT64_MAX / 10)
+			{
+				ec = bdecode_errors::overflow;
+				return start;
+			}
 			val *= 10;
-			val += *start - '0';
+			int digit = *start - '0';
+			if (val > INT64_MAX - digit)
+			{
+				ec = bdecode_errors::overflow;
+				return start;
+			}
+			val += digit;
 			++start;
 		}
+		if (*start != delimiter)
+			ec = bdecode_errors::expected_colon;
 		return start;
 	}
 
@@ -94,7 +121,7 @@ namespace libtorrent
 		std::vector<lazy_entry*> stack;
 
 		stack.push_back(&ret);
-		while (start < end)
+		while (start <= end)
 		{
 			if (stack.empty()) break; // done!
 
@@ -116,11 +143,19 @@ namespace libtorrent
 						stack.pop_back();
 						continue;
 					}
-					if (!is_digit(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_string);
+					if (!numeric(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_string);
 					boost::int64_t len = t - '0';
-					start = parse_int(start, end, ':', len);
-					if (start == 0 || start + len + 3 > end || *start != ':')
-						TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
+					bdecode_errors::error_code_enum e = bdecode_errors::no_error;
+					start = parse_int(start, end, ':', len, e);
+					if (e)
+						TORRENT_FAIL_BDECODE(e);
+
+					if (start + len + 1 > end)
+						TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);
+
+					if (len < 0)
+						TORRENT_FAIL_BDECODE(bdecode_errors::overflow);
+
 					++start;
 					if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);
 					lazy_entry* ent = top->dict_append(start);
@@ -173,13 +208,19 @@ namespace libtorrent
 				}
 				default:
 				{
-					if (!is_digit(t))
+					if (!numeric(t))
 						TORRENT_FAIL_BDECODE(bdecode_errors::expected_value);
 
 					boost::int64_t len = t - '0';
-					start = parse_int(start, end, ':', len);
-					if (start == 0 || start + len + 1 > end || *start != ':')
-						TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
+					bdecode_errors::error_code_enum e = bdecode_errors::no_error;
+					start = parse_int(start, end, ':', len, e);
+					if (e)
+						TORRENT_FAIL_BDECODE(e);
+					if (start + len + 1 > end)
+						TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof);
+					if (len < 0)
+						TORRENT_FAIL_BDECODE(bdecode_errors::overflow);
+
 					++start;
 					top->construct_string(start, int(len));
 					stack.pop_back();
@@ -198,7 +239,10 @@ namespace libtorrent
 		boost::int64_t val = 0;
 		bool negative = false;
 		if (*m_data.start == '-') negative = true;
-		parse_int(negative?m_data.start+1:m_data.start, m_data.start + m_size, 'e', val);
+		bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
+		parse_int(m_data.start + negative
+			, m_data.start + m_size, 'e', val, ec);
+		if (ec) return 0;
 		if (negative) val = -val;
 		return val;
 	}
@@ -331,6 +375,13 @@ namespace libtorrent
 		return e;
 	}
 
+	lazy_entry const* lazy_entry::dict_find_dict(std::string const& name) const
+	{
+		lazy_entry const* e = dict_find(name);
+		if (e == 0 || e->type() != lazy_entry::dict_t) return 0;
+		return e;
+	}
+
 	lazy_entry const* lazy_entry::dict_find_list(char const* name) const
 	{
 		lazy_entry const* e = dict_find(name);
@@ -350,6 +401,19 @@ namespace libtorrent
 		return 0;
 	}
 
+	lazy_entry* lazy_entry::dict_find(std::string const& name)
+	{
+		TORRENT_ASSERT(m_type == dict_t);
+		for (int i = 0; i < int(m_size); ++i)
+		{
+			lazy_dict_entry& e = m_data.dict[i];
+			if (name.size() != e.val.m_begin - e.name) continue;
+			if (std::equal(name.begin(), name.end(), e.name))
+				return &e.val;
+		}
+		return 0;
+	}
+
 	lazy_entry* lazy_entry::list_append()
 	{
 		TORRENT_ASSERT(m_type == list_t);
@@ -492,23 +556,50 @@ namespace libtorrent
 				char const* str = e.string_ptr();
 				for (int i = 0; i < e.string_length(); ++i)
 				{
-					using namespace std;
-					if (is_print((unsigned char)str[i])) continue;
+					char c = str[i];
+					if (c >= 32 && c < 127) continue;
 					printable = false;
 					break;
 				}
 				ret += "'";
 				if (printable)
 				{
-					ret += e.string_value();
+					if (single_line && e.string_length() > 30)
+					{
+						ret.append(e.string_ptr(), 14);
+						ret += "...";
+						ret.append(e.string_ptr() + e.string_length()-14, 14);
+					}
+					else
+						ret.append(e.string_ptr(), e.string_length());
 					ret += "'";
 					return ret;
 				}
-				for (int i = 0; i < e.string_length(); ++i)
+				if (single_line && e.string_length() > 20)
 				{
-					char tmp[5];
-					snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)str[i]);
-					ret += tmp;
+					for (int i = 0; i < 9; ++i)
+					{
+						char tmp[5];
+						snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)str[i]);
+						ret += tmp;
+					}
+					ret += "...";
+					for (int i = e.string_length() - 9
+						, len(e.string_length()); i < len; ++i)
+					{
+						char tmp[5];
+						snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)str[i]);
+						ret += tmp;
+					}
+				}
+				else
+				{
+					for (int i = 0; i < e.string_length(); ++i)
+					{
+						char tmp[5];
+						snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)str[i]);
+						ret += tmp;
+					}
 				}
 				ret += "'";
 				return ret;
@@ -577,6 +668,7 @@ namespace libtorrent
 			"expected value (list, dict, int or string) in bencoded string",
 			"bencoded nesting depth exceeded",
 			"bencoded item count limit exceeded",
+			"integer overflow",
 		};
 		if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0])))
 			return "Unknown error";
@@ -589,5 +681,12 @@ namespace libtorrent
 		return bdecode_category;
 	}
 
+	namespace bdecode_errors
+	{
+		boost::system::error_code make_error_code(error_code_enum e)
+		{
+			return boost::system::error_code(e, get_bdecode_category());
+		}
+	}
 };
 

programming_language: C++
filename: lazy_entry.hpp
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 /*
 
-Copyright (c) 2003-2012, Arvid Norberg
+Copyright (c) 2003-2014, Arvid Norberg
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 #include <string>
 #include <cstring>
+#include <algorithm>
 #include <boost/system/error_code.hpp>
 
 #define TORRENT_EXPORT
@@ -136,6 +137,7 @@ namespace libtorrent
 			none_t, dict_t, list_t, string_t, int_t
 		};
 
+		// internal
 		lazy_entry() : m_begin(0), m_len(0), m_size(0), m_capacity(0), m_type(none_t)
 		{ m_data.start = 0; }
 
@@ -156,7 +158,7 @@ namespace libtorrent
 			m_len = length + 2; // include 'e'
 		}
 
-		// if this is an integer, return the integer value
+		// requires the type to be an integer. return the integer value
 		boost::int64_t int_value() const;
 
 		// internal
@@ -221,6 +223,9 @@ namespace libtorrent
 		lazy_entry* dict_find(char const* name);
 		lazy_entry const* dict_find(char const* name) const
 		{ return const_cast<lazy_entry*>(this)->dict_find(name); }
+		lazy_entry* dict_find(std::string const& name);
+		lazy_entry const* dict_find(std::string const& name) const
+		{ return const_cast<lazy_entry*>(this)->dict_find(name); }
 		lazy_entry const* dict_find_string(char const* name) const;
 
 		// if this is a dictionary, look for a key ``name`` whose value
@@ -235,14 +240,22 @@ namespace libtorrent
 		boost::int64_t dict_find_int_value(char const* name, boost::int64_t default_val = 0) const;
 		lazy_entry const* dict_find_int(char const* name) const;
 
+		// these functions require that ``this`` is a dictionary.
+		// (this->type() == dict_t). They look for an element with the
+		// specified name in the dictionary. ``dict_find_dict`` only
+		// finds dictionaries and ``dict_find_list`` only finds lists.
+		// if no key with the corresponding value of the right type is
+		// found, NULL is returned.
 		lazy_entry const* dict_find_dict(char const* name) const;
+		lazy_entry const* dict_find_dict(std::string const& name) const;
 		lazy_entry const* dict_find_list(char const* name) const;
 
 		// if this is a dictionary, return the key value pair at
 		// position ``i`` from the dictionary.
 		std::pair<std::string, lazy_entry const*> dict_at(int i) const;
 
-		// if this is a dictionary, return the number of items in it
+		// requires that ``this`` is a dictionary. return the
+		// number of items in it
 		int dict_size() const
 		{
 			TORRENT_ASSERT(m_type == dict_t);
@@ -262,7 +275,8 @@ namespace libtorrent
 		// internal
 		lazy_entry* list_append();
 
-		// if this is a list, return the item at index ``i``.
+		// requires that ``this`` is a list. return
+		// the item at index ``i``.
 		lazy_entry* list_at(int i)
 		{
 			TORRENT_ASSERT(m_type == list_t);
@@ -272,8 +286,19 @@ namespace libtorrent
 		lazy_entry const* list_at(int i) const
 		{ return const_cast<lazy_entry*>(this)->list_at(i); }
 
+		// these functions require ``this`` to have the type list.
+		// (this->type() == list_t). ``list_string_value_at`` returns
+		// the string at index ``i``. ``list_pstr_at``
+		// returns a pascal_string of the string value at index ``i``.
+		// if the element at ``i`` is not a string, an empty string
+		// is returned.
 		std::string list_string_value_at(int i) const;
 		pascal_string list_pstr_at(int i) const;
+
+		// this function require ``this`` to have the type list.
+		// (this->type() == list_t). returns the integer value at
+		// index ``i``. If the element at ``i`` is not an integer
+		// ``default_val`` is returned, which defaults to 0.
 		boost::int64_t list_int_value_at(int i, boost::int64_t default_val = 0) const;
 
 		// if this is a list, return the number of items in it.
@@ -283,7 +308,7 @@ namespace libtorrent
 			return int(m_size);
 		}
 
-		// end points one byte passed last byte in the source
+		// internal: end points one byte passed last byte in the source
 		// buffer backing the bencoded structure.
 		void set_end(char const* end)
 		{
@@ -294,7 +319,7 @@ namespace libtorrent
 		// internal
 		void clear();
 
-		// releases ownership of any memory allocated
+		// internal: releases ownership of any memory allocated
 		void release()
 		{
 			m_data.start = 0;
@@ -361,9 +386,12 @@ namespace libtorrent
 		lazy_entry val;
 	};
 
-	TORRENT_EXTRA_EXPORT std::string print_entry(lazy_entry const& e
+	// print the bencoded structure in a human-readable format to a stting
+	// that's returned.
+	TORRENT_EXPORT std::string print_entry(lazy_entry const& e
 		, bool single_line = false, int indent = 0);
 
+	// get the ``error_category`` for bdecode errors
 	TORRENT_EXPORT boost::system::error_category& get_bdecode_category();
 
 	namespace bdecode_errors
@@ -386,17 +414,21 @@ namespace libtorrent
 			depth_exceeded,
 			// bencoded item count limit exceeded
 			limit_exceeded,
+			// integer overflow
+			overflow,
 
 			// the number of error codes
 			error_code_max
 		};
 
 		// hidden
-		inline boost::system::error_code make_error_code(error_code_enum e)
-		{
-			return boost::system::error_code(e, get_bdecode_category());
-		}
+		TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e);
 	}
+
+	TORRENT_EXTRA_EXPORT char const* parse_int(char const* start
+		, char const* end, char delimiter, boost::int64_t& val
+		, bdecode_errors::error_code_enum& ec);
+
 }
 
 #endif

programming_language: C++