From c035397807677ac094c85600b7098e51750fd66e Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Sat, 30 May 2015 14:38:03 +0200 Subject: [PATCH] mkvinfo: fix handling of elements outside a segment Fixes #1183. --- ChangeLog | 5 ++ src/common/kax_file.cpp | 21 +++++- src/common/kax_file.h | 3 +- src/info/mkvinfo.cpp | 155 ++++++++++++++++++++-------------------- 4 files changed, 102 insertions(+), 82 deletions(-) diff --git a/ChangeLog b/ChangeLog index 84678a05e..c3616db63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-05-30 Moritz Bunkus + + * mkvinfo: bug fix: fixed a crash with certain types of invalid + Matroska files. Part of the fix for #1183. + 2015-05-26 Moritz Bunkus * all: bug fix: removed some unused code thereby fixing diff --git a/src/common/kax_file.cpp b/src/common/kax_file.cpp index 5b70ef11f..2d4d5dad0 100644 --- a/src/common/kax_file.cpp +++ b/src/common/kax_file.cpp @@ -31,6 +31,7 @@ kax_file_c::kax_file_c(mm_io_cptr &in) , m_resynced(false) , m_resync_start_pos(0) , m_file_size(m_in->get_size()) + , m_segment_end{} , m_timecode_scale{TIMECODE_SCALE} , m_last_timecode{-1} , m_es(new EbmlStream(*m_in)) @@ -39,6 +40,9 @@ kax_file_c::kax_file_c(mm_io_cptr &in) { } +kax_file_c::~kax_file_c() { +} + EbmlElement * kax_file_c::read_next_level1_element(uint32_t wanted_id, bool report_cluster_timecode) { @@ -69,11 +73,11 @@ kax_file_c::read_next_level1_element(uint32_t wanted_id, return nullptr; } -kax_file_c::~kax_file_c() { -} - EbmlElement * kax_file_c::read_next_level1_element_internal(uint32_t wanted_id) { + if (m_segment_end && (m_in->getFilePointer() >= m_segment_end)) + return nullptr; + m_resynced = false; m_resync_start_pos = 0; @@ -133,6 +137,9 @@ kax_file_c::read_next_level1_element_internal(uint32_t wanted_id) { EbmlElement * kax_file_c::read_one_element() { + if (m_segment_end && (m_in->getFilePointer() >= m_segment_end)) + return nullptr; + int upper_lvl_el = 0; EbmlElement *l1 = m_es->FindNextElement(EBML_CLASS_CONTEXT(KaxSegment), upper_lvl_el, 0xFFFFFFFFL, true); @@ -192,6 +199,9 @@ kax_file_c::resync_to_level1_element(uint32_t wanted_id) { EbmlElement * kax_file_c::resync_to_level1_element_internal(uint32_t wanted_id) { + if (m_segment_end && (m_in->getFilePointer() >= m_segment_end)) + return nullptr; + m_resynced = true; m_resync_start_pos = m_in->getFilePointer(); @@ -323,3 +333,8 @@ void kax_file_c::set_last_timecode(int64_t last_timecode) { m_last_timecode = last_timecode; } + +void +kax_file_c::set_segment_end(EbmlElement const &segment) { + m_segment_end = segment.IsFiniteSize() ? segment.GetElementPosition() + segment.HeadSize() + segment.GetSize() : m_in->get_size(); +} diff --git a/src/common/kax_file.h b/src/common/kax_file.h index d463c6559..77eb9055f 100644 --- a/src/common/kax_file.h +++ b/src/common/kax_file.h @@ -28,7 +28,7 @@ class kax_file_c { protected: mm_io_cptr m_in; bool m_resynced; - uint64_t m_resync_start_pos, m_file_size; + uint64_t m_resync_start_pos, m_file_size, m_segment_end; int64_t m_timecode_scale, m_last_timecode; std::shared_ptr m_es; @@ -53,6 +53,7 @@ class kax_file_c { virtual void set_timecode_scale(int64_t timecode_scale); virtual void set_last_timecode(int64_t last_timecode); + virtual void set_segment_end(EbmlElement const &segment); protected: virtual EbmlElement *read_one_element(); diff --git a/src/info/mkvinfo.cpp b/src/info/mkvinfo.cpp index 627564131..b48853199 100644 --- a/src/info/mkvinfo.cpp +++ b/src/info/mkvinfo.cpp @@ -1560,6 +1560,67 @@ handle_ebml_head(EbmlElement *l0, } } +void +handle_segment(EbmlElement *l0, + mm_io_cptr &in, + EbmlStream *es) { + auto file_size = in->get_size(); + auto l1 = static_cast(nullptr); + auto upper_lvl_el = 0; + kax_file_cptr kax_file = kax_file_cptr(new kax_file_c(in)); + + kax_file->set_segment_end(*l0); + + if (!l0->IsFiniteSize()) + show_element(l0, 0, Y("Segment, size unknown")); + else + show_element(l0, 0, boost::format(Y("Segment, size %1%")) % l0->GetSize()); + + // Prevent reporting "first timecode after resync": + kax_file->set_timecode_scale(-1); + + while ((l1 = kax_file->read_next_level1_element())) { + std::shared_ptr af_l1(l1); + + if (Is(l1)) + handle_info(es, upper_lvl_el, l1); + + else if (Is(l1)) + handle_tracks(es, upper_lvl_el, l1); + + else if (Is(l1)) + handle_seek_head(es, upper_lvl_el, l1); + + else if (Is(l1)) { + show_element(l1, 1, Y("Cluster")); + if ((g_options.m_verbose == 0) && !g_options.m_show_summary) + return; + handle_cluster(es, upper_lvl_el, l1, file_size); + + } else if (Is(l1)) + handle_cues(es, upper_lvl_el, l1); + + // Weee! Attachments! + else if (Is(l1)) + handle_attachments(es, upper_lvl_el, l1); + + else if (Is(l1)) + handle_chapters(es, upper_lvl_el, l1); + + // Let's handle some TAGS. + else if (Is(l1)) + handle_tags(es, upper_lvl_el, l1); + + else if (!is_global(es, l1, 1)) + show_unknown_element(l1, 1); + + if (!in->setFilePointer2(l1->GetElementPosition() + kax_file->get_element_size(l1))) + break; + if (!in_parent(l0)) + break; + } // while (l1) +} + void display_track_info() { if (!g_options.m_show_track_info) @@ -1587,9 +1648,7 @@ display_track_info() { bool process_file(const std::string &file_name) { - int upper_lvl_el; // Elements for different levels - EbmlElement *l0 = nullptr, *l1 = nullptr; s_tc_scale = TIMECODE_SCALE; s_tracks.clear(); @@ -1605,100 +1664,40 @@ process_file(const std::string &file_name) { return false; } - in->setFilePointer(0, seek_end); - uint64_t file_size = in->getFilePointer(); - in->setFilePointer(0, seek_beginning); - try { - EbmlStream *es = new EbmlStream(*in); + auto es_ptr = std::make_shared(*in); + auto es = es_ptr.get(); // Find the EbmlHead element. Must be the first one. - l0 = es->FindNextID(EBML_INFO(EbmlHead), 0xFFFFFFFFL); - if (!l0 || !Is(l0)) { + auto l0 = ebml_element_cptr{ es->FindNextID(EBML_INFO(EbmlHead), 0xFFFFFFFFL) }; + if (!l0 || !Is(*l0)) { show_error(Y("No EBML head found.")); - delete es; - return false; } - handle_ebml_head(l0, in, es); + handle_ebml_head(l0.get(), in, es); l0->SkipData(*es, EBML_CONTEXT(l0)); - delete l0; while (1) { // NEXT element must be a segment - l0 = es->FindNextID(EBML_INFO(KaxSegment), 0xFFFFFFFFFFFFFFFFLL); - if (!l0) { - show_error(Y("No segment/level 0 element found.")); - return false; - } - - if (Is(l0)) { - if (!l0->IsFiniteSize()) - show_element(l0, 0, Y("Segment, size unknown")); - else - show_element(l0, 0, boost::format(Y("Segment, size %1%")) % l0->GetSize()); + l0 = ebml_element_cptr{ es->FindNextID(EBML_INFO(KaxSegment), 0xFFFFFFFFFFFFFFFFLL) }; + if (!l0) break; - } - - show_element(l0, 0, boost::format(Y("Next level 0 element is not a segment but %1%")) % typeid(*l0).name()); - - l0->SkipData(*es, EBML_CONTEXT(l0)); - delete l0; - } - - kax_file_cptr kax_file = kax_file_cptr(new kax_file_c(in)); - - // Prevent reporting "first timecode after resync": - kax_file->set_timecode_scale(-1); - - while ((l1 = kax_file->read_next_level1_element())) { - std::shared_ptr af_l1(l1); - - if (Is(l1)) - handle_info(es, upper_lvl_el, l1); - - else if (Is(l1)) - handle_tracks(es, upper_lvl_el, l1); - else if (Is(l1)) - handle_seek_head(es, upper_lvl_el, l1); + if (!Is(*l0)) { + show_element(l0.get(), 0, Y("Unknown element")); + l0->SkipData(*es, EBML_CONTEXT(l0)); - else if (Is(l1)) { - show_element(l1, 1, Y("Cluster")); - if ((g_options.m_verbose == 0) && !g_options.m_show_summary) { - delete l0; - delete es; - - return true; - } - handle_cluster(es, upper_lvl_el, l1, file_size); - - } else if (Is(l1)) - handle_cues(es, upper_lvl_el, l1); - - // Weee! Attachments! - else if (Is(l1)) - handle_attachments(es, upper_lvl_el, l1); - - else if (Is(l1)) - handle_chapters(es, upper_lvl_el, l1); + continue; + } - // Let's handle some TAGS. - else if (Is(l1)) - handle_tags(es, upper_lvl_el, l1); + handle_segment(l0.get(), in, es); - else if (!is_global(es, l1, 1)) - show_unknown_element(l1, 1); + l0->SkipData(*es, EBML_CONTEXT(l0)); - if (!in->setFilePointer2(l1->GetElementPosition() + kax_file->get_element_size(l1))) - break; - if (!in_parent(l0)) + if ((g_options.m_verbose == 0) && !g_options.m_show_summary) break; - } // while (l1) - - delete l0; - delete es; + } if (!g_options.m_use_gui && g_options.m_show_track_info) display_track_info();