CVE-2014-8509 (C++, published 2014-10-31T14:55Z)
Description: value: 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 v3 base score: )
Weakness: CWE-119 \u2014 Improper Restriction of Operations within the Bounds of a Memory Buffer
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.
Repository: https://github.com/bittorrent/bootstrap-dht
Files changed:
lazy_bdecode.cpp
lazy_entry.hpp
Commit message: update version of lazy_bdecode from libtorrent


Vulnerable code:
Copyright (c) 2008-2012, Arvid Norberg
#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; \
	bool is_digit(char c) { return c >= '0' && c <= '9'; }
	bool is_print(char c) { return c >= 32 && c < 127; }
	char const* parse_int(char const* start, char const* end, char delimiter, boost::int64_t& val)
			if (!is_digit(*start)) { return 0; }
			val += *start - '0';
		while (start < end)
					if (!is_digit(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_string);
					start = parse_int(start, end, ':', len);
					if (start == 0 || start + len + 3 > end || *start != ':')
						TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
					if (!is_digit(t))
					start = parse_int(start, end, ':', len);
					if (start == 0 || start + len + 1 > end || *start != ':')
						TORRENT_FAIL_BDECODE(bdecode_errors::expected_colon);
		parse_int(negative?m_data.start+1:m_data.start, m_data.start + m_size, 'e', val);
					using namespace std;
					if (is_print((unsigned char)str[i])) continue;
					ret += e.string_value();
				for (int i = 0; i < e.string_length(); ++i)
					char tmp[5];
					snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)str[i]);
					ret += tmp;
Copyright (c) 2003-2012, Arvid Norberg
		// if this is an integer, return the integer value
		// if this is a dictionary, return the number of items in it
		// if this is a list, return the item at index ``i``.
		// end points one byte passed last byte in the source
		// releases ownership of any memory allocated
	TORRENT_EXTRA_EXPORT std::string print_entry(lazy_entry const& e
		inline boost::system::error_code make_error_code(error_code_enum e)
		{
			return boost::system::error_code(e, get_bdecode_category());
		}

Fixed code:
Copyright (c) 2008-2014, Arvid Norberg
	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;
		}
#define TORRENT_FAIL_BDECODE(code) do { ec = make_error_code(code); return fail(error_pos, stack, start, orig_start); } while (false)
	namespace { bool numeric(char c) { return c >= '0' && c <= '9'; } }
	char const* parse_int(char const* start, char const* end, char delimiter
		, boost::int64_t& val, bdecode_errors::error_code_enum& ec)
			if (!numeric(*start))
			{
				ec = bdecode_errors::expected_string;
				return start;
			}
			if (val > INT64_MAX / 10)
			{
				ec = bdecode_errors::overflow;
				return start;
			}
			int digit = *start - '0';
			if (val > INT64_MAX - digit)
			{
				ec = bdecode_errors::overflow;
				return start;
			}
			val += digit;
		if (*start != delimiter)
			ec = bdecode_errors::expected_colon;
		while (start <= end)
					if (!numeric(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_string);
					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);

					if (!numeric(t))
					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);

		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;
	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* 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;
	}

					char c = str[i];
					if (c >= 32 && c < 127) continue;
					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());
				if (single_line && e.string_length() > 20)
					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;
					}
			"integer overflow",
	namespace bdecode_errors
	{
		boost::system::error_code make_error_code(error_code_enum e)
		{
			return boost::system::error_code(e, get_bdecode_category());
		}
	}
Copyright (c) 2003-2014, Arvid Norberg
#include <algorithm>
		// internal
		// requires the type to be an integer. return the integer value
		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); }
		// 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(std::string const& name) const;
		// requires that ``this`` is a dictionary. return the
		// number of items in it
		// requires that ``this`` is a list. return
		// the item at index ``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.

		// 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.
		// internal: end points one byte passed last byte in the source
		// internal: releases ownership of any memory allocated
	// 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
	// get the ``error_category`` for bdecode errors
			// integer overflow
			overflow,
		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);