diff --git a/libtransmission/variant-benc.cc b/libtransmission/variant-benc.cc index 56a26940e..26c253c15 100644 --- a/libtransmission/variant-benc.cc +++ b/libtransmission/variant-benc.cc @@ -6,7 +6,6 @@ #include #include /* isdigit() */ #include -#include #include #include @@ -144,26 +143,31 @@ struct MyHandler : public transmission::benc::Handler bool Int64(int64_t value, Context const& /*context*/) final { - if (tr_variant* variant = get_node(); variant != nullptr) + auto* const variant = get_node(); + if (variant == nullptr) { - tr_variantInitInt(variant, value); + return false; } + tr_variantInitInt(variant, value); return true; } bool String(std::string_view sv, Context const& /*context*/) final { - if (tr_variant* variant = get_node(); variant != nullptr) + auto* const variant = get_node(); + if (variant == nullptr) { - if ((parse_opts_ & TR_VARIANT_PARSE_INPLACE) != 0) - { - tr_variantInitStrView(variant, sv); - } - else - { - tr_variantInitStr(variant, sv); - } + return false; + } + + if ((parse_opts_ & TR_VARIANT_PARSE_INPLACE) != 0) + { + tr_variantInitStrView(variant, sv); + } + else + { + tr_variantInitStr(variant, sv); } return true; @@ -171,12 +175,14 @@ struct MyHandler : public transmission::benc::Handler bool StartDict(Context const& /*context*/) final { - if (tr_variant* variant = get_node(); variant != nullptr) + auto* const variant = get_node(); + if (variant == nullptr) { - tr_variantInitDict(variant, 0); - stack_.push_back(variant); + return false; } + tr_variantInitDict(variant, 0); + stack_.push_back(variant); return true; } @@ -189,26 +195,36 @@ struct MyHandler : public transmission::benc::Handler bool EndDict(Context const& /*context*/) final { - stack_.pop_back(); + if (std::empty(stack_)) + { + return false; + } + stack_.pop_back(); return true; } bool StartArray(Context const& /*context*/) final { - if (tr_variant* variant = get_node(); variant != nullptr) + auto* const variant = get_node(); + if (variant == nullptr) { - tr_variantInitList(variant, 0); - stack_.push_back(variant); + return false; } + tr_variantInitList(variant, 0); + stack_.push_back(variant); return true; } bool EndArray(Context const& /*context*/) final { - stack_.pop_back(); + if (std::empty(stack_)) + { + return false; + } + stack_.pop_back(); return true; } @@ -221,19 +237,14 @@ private: { node = top_; } - else + else if (auto* parent = stack_.back(); tr_variantIsList(parent)) { - auto* parent = stack_.back(); - - if (tr_variantIsList(parent)) - { - node = tr_variantListAdd(parent); - } - else if (key_ && tr_variantIsDict(parent)) - { - node = tr_variantDictAdd(parent, *key_); - key_.reset(); - } + node = tr_variantListAdd(parent); + } + else if (key_ && tr_variantIsDict(parent)) + { + node = tr_variantDictAdd(parent, *key_); + key_.reset(); } return node; @@ -245,12 +256,7 @@ bool tr_variantParseBenc(tr_variant& top, int parse_opts, std::string_view benc, using Stack = transmission::benc::ParserStack<512>; auto stack = Stack{}; auto handler = MyHandler{ &top, parse_opts }; - bool const ok = transmission::benc::parse(benc, stack, handler, setme_end, error); - if (!ok) - { - tr_variantFree(&top); - } - return ok; + return transmission::benc::parse(benc, stack, handler, setme_end, error); } /**** diff --git a/tests/libtransmission/torrent-metainfo-test.cc b/tests/libtransmission/torrent-metainfo-test.cc index ad675d134..1c3c8444f 100644 --- a/tests/libtransmission/torrent-metainfo-test.cc +++ b/tests/libtransmission/torrent-metainfo-test.cc @@ -4,12 +4,12 @@ // License text can be found in the licenses/ folder. #include -#include #include #include #include "transmission.h" +#include "crypto-utils.h" #include "error.h" #include "torrent-metainfo.h" #include "torrent.h" @@ -85,6 +85,34 @@ TEST_F(TorrentMetainfoTest, bucket) } } +TEST_F(TorrentMetainfoTest, parseBencFuzzRegressions) +{ + static auto constexpr Tests = std::array{ + "ZC/veNSVW0Ss+KGfMqH4DQqtYXzgmVi5oBi0XlxviLytlwwjf7MLanOcnS73eSB/iye83hVyvSWg27tPl5oqWdNEZ0euMbo7E8FH/xgTvUEOnBVgvPno50CyI7c5F2QTw16avUB7dvGzx5xIjzJ2qkD2BsNtOoiZI3skC6XwSifsDfJUN8NxHFiwvWxmZRLq2eQlE2wGxAW5aLj6U1MHDzPZ83+2o81pRyMr11bHmWFcNorTGLeOpHBd9veduHpNOKNwOatoXeb57jZCy1Zmu9y/wCuUx6DP3I5FGQ/t3AYh7w028Z/zgIlvWat6QjqSPp7j1nEbl6SNZNl1doGmusl9hvRsbaCq9b1XHpTDtQSJ8Owj07fph0p0ZVu5kJpQBfOGsHLh6ALVrTepptIvcnNW9+nauE+NJa2z+9Yla7780sCdBsGYZZA6HUr0J9GXES7+uRPPBwAl2YB1qWhCsOCClixTiAlwrsBl1bJ/a4FV04aU5jXDEYrpJMzdSAEoypDWMsn3Fc5umLqJ1jtqPqykKY0HjPrCkVAMmvmacauBzIj5Eg/uw0xtZp+wXdLQv8qyuXgsJs7dExZbgTgfPY4niTBpftM6YFQrCx/IxiMshYp7tMolykoed/8gZMm6yyWizzml4BlvnvY3+J2eVKRvS7QToRKxN5eFP9l/pflrK+8cHbwVnjQ1pE3hTQACmNIQHRTY2QoOGwG+HTwo48akfbJnjJ3F0iN6miy7lvv5u0p1rpbM2On5FJ3G98OYnzGIxf8BomHvVp/3eX6QJZUMZKsUTpgbRqg0AJH9FjiERQ9v6B25Va+Q0yV8z5DmiA5AgyIwkIzlSBAl0PYsNaw+rH06a93yBhAfK6EPSArYLjMI6o/1kF4UxNyfE+F79xbdCAKRAX3iJ7DH1GncFoIQ1fZd/uZaF9tXjViQ7P/sHuKdZvfLpvJq88JV5Pcdsfdlle86QAF4weB+k/k8f/xgvxRNbbcAfjLvEHhDBzfEvHkgFrW19WvLHyAqjjUovpecIu3KeCqwyOr1dHViUVelxqc5BklyGQ+Asd6GnWPSzO5Hamj4rYrapgogEup5PKm1j2CgL2HH2tySWwjgtOWbooGhsdBnCeQOsapCxwc6ALtudG4Q9RBu6A6pLUfFE3rm1RuvNGoJNHiEQ4BAFiqLpYJd4lR7V2fI6EIKug0dB3SpHpUeNCQbG67IM+kVe0I+vP3cECGOGXo="sv, + }; + + for (auto const& test : Tests) + { + auto tm = tr_torrent_metainfo{}; + tm.parseBenc(tr_base64_decode(test)); + } +} + +TEST_F(TorrentMetainfoTest, parseBencFuzz) +{ + auto buf = std::vector{}; + + for (size_t i = 0; i < 100000; ++i) + { + buf.resize(tr_rand_int(1024)); + tr_rand_buffer(std::data(buf), std::size(buf)); + // std::cerr << '[' << tr_base64_encode({ std::data(buf), std::size(buf) }) << ']' << std::endl; + + auto tm = tr_torrent_metainfo{}; + tm.parseBenc({ std::data(buf), std::size(buf) }); + } +} + #if 0 TEST_F(TorrentMetainfoTest, sanitize) { diff --git a/tests/libtransmission/web-utils-test.cc b/tests/libtransmission/web-utils-test.cc index a059d8cdf..f483fd29c 100644 --- a/tests/libtransmission/web-utils-test.cc +++ b/tests/libtransmission/web-utils-test.cc @@ -12,6 +12,8 @@ #endif #include "transmission.h" + +#include "crypto-utils.h" #include "platform.h" #include "web-utils.h" @@ -128,6 +130,18 @@ TEST_F(WebUtilsTest, urlParse) EXPECT_EQ(8080, parsed->port); } +TEST(WebUtilsTest, urlParseFuzz) +{ + auto buf = std::vector{}; + + for (size_t i = 0; i < 100000; ++i) + { + buf.resize(tr_rand_int(1024)); + tr_rand_buffer(std::data(buf), std::size(buf)); + tr_urlParse({ std::data(buf), std::size(buf) }); + } +} + TEST_F(WebUtilsTest, urlNextQueryPair) { auto constexpr Query = "a=1&b=two&c=si&d_has_no_val&e=&f&g=gee"sv;