1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-24 08:43:27 +00:00

fix: tr_clientForId() (#2887)

* fix: memory corruption when parsing negative numbers

* fix: stack-buffer-overflow on escape chars in tr_clientForId

* test: add tr_clientForId() fuzz tests
This commit is contained in:
Charles Kerr 2022-04-06 12:06:11 -05:00 committed by GitHub
parent 7ff1382503
commit eb33b2faf5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 146 additions and 30 deletions

View file

@ -72,24 +72,112 @@ constexpr std::pair<char*, size_t> buf_append(char* buf, size_t buflen, T t, Arg
return buf_append(buf, buflen, args...); return buf_append(buf, buflen, args...);
} }
// ['0'..'9']: ch - '0' constexpr std::string_view charint(char ch)
// ['A'..'Z']: 10 + ch - '9' {
// ['a'..'z']: 36 + ch - '9' switch (ch)
auto constexpr charints = std::array<std::string_view, 256>{ {
{ "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", case '0':
"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", return "0"sv;
"x", "x", "x", "x", "x", "x", "x", "x", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "x", "x", case '1':
"x", "x", "x", "x", "x", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", return "1"sv;
"25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "x", "x", "x", "x", "x", "x", "36", "37", "38", case '2':
"39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", return "2"sv;
"59", "60", "61", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", case '3':
"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", return "3"sv;
"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", case '4':
"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", return "4"sv;
"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", case '5':
"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", return "5"sv;
"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x" } case '6':
}; return "6"sv;
case '7':
return "7"sv;
case '8':
return "8"sv;
case '9':
return "9"sv;
case 'a':
case 'A':
return "10"sv;
case 'b':
case 'B':
return "11"sv;
case 'c':
case 'C':
return "12"sv;
case 'd':
case 'D':
return "13"sv;
case 'e':
case 'E':
return "14"sv;
case 'f':
case 'F':
return "15"sv;
case 'g':
case 'G':
return "16"sv;
case 'h':
case 'H':
return "17"sv;
case 'i':
case 'I':
return "18"sv;
case 'j':
case 'J':
return "19"sv;
case 'k':
case 'K':
return "20"sv;
case 'l':
case 'L':
return "21"sv;
case 'm':
case 'M':
return "22"sv;
case 'n':
case 'N':
return "23"sv;
case 'o':
case 'O':
return "24"sv;
case 'p':
case 'P':
return "25"sv;
case 'q':
case 'Q':
return "26"sv;
case 'r':
case 'R':
return "27"sv;
case 's':
case 'S':
return "28"sv;
case 't':
case 'T':
return "29"sv;
case 'u':
case 'U':
return "30"sv;
case 'v':
case 'V':
return "31"sv;
case 'w':
case 'W':
return "32"sv;
case 'x':
case 'X':
return "33"sv;
case 'y':
case 'Y':
return "34"sv;
case 'z':
case 'Z':
return "35"sv;
default:
return "x"sv;
}
}
int strint(void const* pch, int span, int base = 0) int strint(void const* pch, int span, int base = 0)
{ {
@ -249,15 +337,15 @@ using format_func = void (*)(char* buf, size_t buflen, std::string_view name, tr
constexpr void three_digit_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void three_digit_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', charints[id[4]], '.', charints[id[5]]); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', charint(id[4]), '.', charint(id[5]));
} }
constexpr void four_digit_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void four_digit_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', charints[id[4]], '.', charints[id[5]], '.', charints[id[6]]); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', charint(id[4]), '.', charint(id[5]), '.', charint(id[6]));
} }
constexpr void no_version_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t /*id*/) void no_version_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t /*id*/)
{ {
buf_append(buf, buflen, name); buf_append(buf, buflen, name);
} }
@ -344,23 +432,23 @@ constexpr void burst_formatter(char* buf, size_t buflen, std::string_view name,
constexpr void ctorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void ctorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', charints[id[4]], '.', id[5], id[6]); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', charint(id[4]), '.', id[5], id[6]);
} }
constexpr void folx_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void folx_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', 'x'); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', 'x');
} }
constexpr void ktorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void ktorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
if (id[5] == 'D') if (id[5] == 'D')
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', charints[id[4]], " Dev "sv, charints[id[6]]); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', charint(id[4]), " Dev "sv, charint(id[6]));
} }
else if (id[5] == 'R') else if (id[5] == 'R')
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', charints[id[4]], " RC "sv, charints[id[6]]); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', charint(id[4]), " RC "sv, charint(id[6]));
} }
else else
{ {
@ -389,7 +477,7 @@ constexpr void mainline_formatter(char* buf, size_t buflen, std::string_view nam
constexpr void mediaget_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void mediaget_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', charints[id[4]]); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', charint(id[4]));
} }
constexpr void mldonkey_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void mldonkey_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
@ -411,7 +499,7 @@ constexpr void opera_formatter(char* buf, size_t buflen, std::string_view name,
constexpr void picotorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void picotorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
buf_append(buf, buflen, name, ' ', charints[id[3]], '.', id[4], id[5], '.', charints[id[6]]); buf_append(buf, buflen, name, ' ', charint(id[3]), '.', id[4], id[5], '.', charint(id[6]));
} }
constexpr void plus_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void plus_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
@ -421,7 +509,7 @@ constexpr void plus_formatter(char* buf, size_t buflen, std::string_view name, t
constexpr void qvod_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) constexpr void qvod_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
buf_append(buf, buflen, name, ' ', charints[id[4]], '.', charints[id[5]], '.', charints[id[6]], '.', charints[id[7]]); buf_append(buf, buflen, name, ' ', charint(id[4]), '.', charint(id[5]), '.', charint(id[6]), '.', charint(id[7]));
} }
void transmission_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) void transmission_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
@ -500,7 +588,7 @@ constexpr void xfplay_formatter(char* buf, size_t buflen, std::string_view name,
void xtorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id) void xtorrent_formatter(char* buf, size_t buflen, std::string_view name, tr_peer_id_t id)
{ {
std::tie(buf, buflen) = buf_append(buf, buflen, name, ' ', charints[id[3]], '.', charints[id[4]], " ("sv); std::tie(buf, buflen) = buf_append(buf, buflen, name, ' ', charint(id[3]), '.', charint(id[4]), " ("sv);
*fmt::format_to_n(buf, buflen - 1, FMT_STRING("{:d}"), strint(&id[5], 2)).out = '\0'; *fmt::format_to_n(buf, buflen - 1, FMT_STRING("{:d}"), strint(&id[5], 2)).out = '\0';
} }
@ -699,7 +787,7 @@ char* tr_clientForId(char* buf, size_t buflen, tr_peer_id_t peer_id)
} }
else else
{ {
walk = fmt::format_to_n(walk, end - walk - 1, FMT_STRING("%{:02X}"), unsigned(c)).out; walk = fmt::format_to_n(walk, end - walk - 1, FMT_STRING("%{:02X}"), static_cast<unsigned char>(c)).out;
} }
} }

View file

@ -74,3 +74,31 @@ TEST(Client, clientForId)
EXPECT_EQ(test.expected_client, std::string_view{ buf.data() }); EXPECT_EQ(test.expected_client, std::string_view{ buf.data() });
} }
} }
TEST(Client, fuzzRegressions)
{
auto constexpr Tests = std::array<std::string_view, 5>{
"LVJTp3u+Aptl01HjzTHXVC5b9g4="sv, "LWJrHb2OpoNsJdODHA7iyXjnHxc="sv, "LU1PjpTjmvUth+f15YTOOggXl3k="sv,
"LUxU1gO7xhfBD4bmyZkB+neZIx0="sv, "LVJTp3u+Aptl01HjzTHXVC5b9g4="sv,
};
for (auto const& test : Tests)
{
auto const input = tr_base64_decode(test);
auto peer_id = tr_peer_id_t{};
std::copy(std::begin(input), std::end(input), std::begin(peer_id));
auto buf = std::array<char, 128>{};
tr_clientForId(buf.data(), buf.size(), peer_id);
}
}
TEST(Client, fuzz)
{
for (size_t i = 0; i < 10000; ++i)
{
auto peer_id = tr_peer_id_t{};
tr_rand_buffer(std::data(peer_id), std::size(peer_id));
auto buf = std::array<char, 128>{};
tr_clientForId(buf.data(), buf.size(), peer_id);
}
}