diff --git a/tests/ogg_test.cc b/tests/ogg_test.cc index b20476a..bc23f8f 100644 --- a/tests/ogg_test.cc +++ b/tests/ogg_test.cc @@ -80,5 +80,59 @@ TEST_CASE("decoding a multi-stream file", "[ogg]") REQUIRE(s->state == ogg::TAGS_READY); } +void craft_stream(std::ostream &out, const std::string &tags_data) +{ + ogg::Encoder enc(out); + ogg_stream_state os; + ogg_page og; + ogg_packet op; + ogg_stream_init(&os, 0); + + op.packet = reinterpret_cast(const_cast("OpusHead")); + op.bytes = 8; + op.b_o_s = 1; + op.e_o_s = 0; + op.granulepos = 0; + op.packetno = 0; + ogg_stream_packetin(&os, &op); + ogg_stream_flush(&os, &og); + enc.write_raw_page(og); + + op.packet = reinterpret_cast(const_cast(tags_data.data())); + op.bytes = tags_data.size(); + op.b_o_s = 0; + op.e_o_s = 1; + op.granulepos = 0; + op.packetno = 1; + ogg_stream_packetin(&os, &op); + ogg_stream_flush(&os, &og); + enc.write_raw_page(og); + + ogg_stream_clear(&os); +} + +const char *evil_tags = + "OpusTags" + "\x00\x00\x00\x00" "" /* vendor */ + "\x01\x00\x00\x00" /* one comment */ + "\xFA\x00\x00\x00" "TITLE=Evil" + /* ^ should be \x0A as the length of the comment is 10 */ +; + +TEST_CASE("decoding a malicious Ogg Opus file", "[ogg]") +{ + std::stringstream buf; + craft_stream(buf, std::string(evil_tags, 8 + 4 + 4 + 4 + 10)); + buf.seekg(0, buf.beg); + ogg::Decoder dec(buf); + + std::shared_ptr s = dec.read_page(); + REQUIRE(s != nullptr); + REQUIRE(s->state == ogg::HEADER_READY); + REQUIRE(s->type == ogg::OPUS_STREAM); + + REQUIRE_THROWS(dec.read_page()); +} + // Encoding is trickier, and might as well be done in actions_test.cc, given // opustags::edit_tags covers all of Encoder's regular code.