Coverage Report

Created: 2026-05-30 08:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/mkv/Ebml_parser.cpp
Line
Count
Source
1
2
/*****************************************************************************
3
 * EbmlParser for the matroska demuxer
4
 *****************************************************************************
5
 * Copyright (C) 2003-2004 VLC authors and VideoLAN
6
 *
7
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Steve Lhomme <steve.lhomme@free.fr>
9
 *
10
 * This program is free software; you can redistribute it and/or modify it
11
 * under the terms of the GNU Lesser General Public License as published by
12
 * the Free Software Foundation; either version 2.1 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with this program; if not, write to the Free Software Foundation,
22
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
 *****************************************************************************/
24
25
#include "Ebml_parser.hpp"
26
#include "stream_io_callback.hpp"
27
28
namespace mkv {
29
30
/*****************************************************************************
31
 * Ebml Stream parser
32
 *****************************************************************************/
33
EbmlParser::EbmlParser( matroska_iostream_c *es, EbmlElement *el_start, demux_t *p_demux ) :
34
7.00k
    p_demux( p_demux ),
35
7.00k
    m_es( es ),
36
7.00k
    mi_level( 1 ),
37
7.00k
    mi_user_level( 1 ),
38
7.00k
    mb_keep( false ),
39
7.00k
    mb_dummy( var_InheritBool( p_demux, "mkv-use-dummy" ) )
40
7.00k
{
41
7.00k
    memset( m_el, 0, sizeof( *m_el ) * M_EL_MAXSIZE);
42
7.00k
    m_el[0] = el_start;
43
7.00k
}
44
45
EbmlParser::~EbmlParser( void )
46
7.00k
{
47
7.00k
    if( !mi_level )
48
1.34k
    {
49
1.34k
        assert( !mb_keep );
50
1.34k
        delete m_el[1];
51
1.34k
        return;
52
1.34k
    }
53
54
14.3k
    for( size_t i = 1; i <= mi_level; i++ )
55
8.70k
    {
56
8.70k
        if( !mb_keep )
57
8.70k
        {
58
8.70k
            delete m_el[i];
59
8.70k
        }
60
8.70k
        mb_keep = false;
61
8.70k
    }
62
5.65k
}
63
64
void EbmlParser::reconstruct( matroska_iostream_c* es, EbmlElement* el_start, demux_t* p_demux )
65
3.22k
{
66
3.22k
    this->~EbmlParser();
67
68
3.22k
    new( static_cast<void*>( this ) ) EbmlParser( es, el_start, p_demux );
69
3.22k
}
70
71
void EbmlParser::Up( void )
72
92.9k
{
73
92.9k
    if( mi_user_level == mi_level && m_el[mi_level] )
74
7
    {
75
7
        msg_Warn( p_demux, "MKV/Ebml Parser: Up cannot escape itself" );
76
7
    }
77
78
92.9k
    assert(mi_user_level != 0);
79
92.9k
    mi_user_level--;
80
92.9k
}
81
82
void EbmlParser::Down( void )
83
95.3k
{
84
95.3k
    assert(m_el[mi_level] != nullptr);
85
95.3k
    mi_user_level++;
86
95.3k
    mi_level++;
87
95.3k
}
88
89
void EbmlParser::Keep( void )
90
35.6k
{
91
35.6k
    mb_keep = true;
92
35.6k
}
93
94
void EbmlParser::Unkeep()
95
2.62k
{
96
2.62k
    mb_keep = false;
97
2.62k
}
98
99
int EbmlParser::GetLevel( void ) const
100
268k
{
101
268k
    return mi_user_level;
102
268k
}
103
104
void EbmlParser::Reset( demux_t *p_demux )
105
2.34k
{
106
4.56k
    while ( mi_level > 0)
107
2.21k
    {
108
2.21k
        delete m_el[mi_level];
109
2.21k
        m_el[mi_level] = NULL;
110
2.21k
        mi_level--;
111
2.21k
    }
112
2.34k
    this->p_demux = p_demux;
113
2.34k
    mi_user_level = mi_level = 1;
114
2.34k
    return_previous_parent = false;
115
    // a little faster and cleaner
116
2.34k
    m_es->I_O().setFilePointer( static_cast<EbmlMaster*>(m_el[0])->GetDataStart() );
117
2.34k
}
118
119
120
static const EbmlSemanticContext & GetEbmlNoGlobal_Context();
121
static const EbmlSemanticContext EbmlNoGlobal_Context = EbmlSemanticContext(0, NULL, NULL, *GetEbmlNoGlobal_Context, NULL);
122
static const EbmlSemanticContext & GetEbmlNoGlobal_Context()
123
0
{
124
0
  return EbmlNoGlobal_Context;
125
0
}
126
127
// the Segment Context should not allow Void or CRC32 elements to avoid lookup false alarm
128
DEFINE_START_SEMANTIC(KaxSegmentVLC)
129
DEFINE_SEMANTIC_ITEM(true, true, EbmlHead)
130
DEFINE_SEMANTIC_ITEM(false, false, KaxSegment)
131
DEFINE_END_SEMANTIC(KaxSegmentVLC)
132
133
DEFINE_xxx_CONTEXT(KaxSegmentVLC,GetEbmlNoGlobal_Context)
134
135
136
EbmlElement *EbmlParser::Get( bool allow_overshoot )
137
453k
{
138
453k
    int i_ulev = 0;
139
453k
    int n_call = 0;
140
453k
    EbmlElement *p_prev = NULL;
141
453k
    bool do_read = true;
142
143
453k
    if( mi_user_level != mi_level )
144
42.7k
    {
145
42.7k
        return NULL;
146
42.7k
    }
147
410k
    if( return_previous_parent )
148
66.2k
    {
149
66.2k
        EbmlElement *ret = m_el[mi_level];
150
66.2k
        return_previous_parent = false;
151
152
66.2k
        if( mi_level > 0 && m_el[mi_level-1]->IsFiniteSize() )
153
55.7k
        {
154
55.7k
            if( ret->IsFiniteSize() &&
155
55.7k
                ret->GetEndPosition() > m_el[mi_level-1]->GetEndPosition() )
156
299
            {
157
299
                msg_Err( p_demux, "EBML element at %" PRIu64 " extends beyond parent boundary (%" PRIu64 " beyond %" PRIu64 ")",
158
299
                         ret->GetElementPosition(), ret->GetEndPosition(), m_el[mi_level-1]->GetEndPosition() );
159
299
                delete ret;
160
299
                m_el[mi_level] = NULL;
161
299
                return NULL;
162
299
            }
163
55.4k
            if( !ret->IsFiniteSize() )
164
4
            {
165
4
                msg_Err( p_demux, "Infinite EBML element %s at %" PRIu64 " inside finite parent",
166
4
                         EBML_NAME(ret), ret->GetElementPosition() );
167
4
                delete ret;
168
4
                m_el[mi_level] = NULL;
169
4
                return NULL;
170
4
            }
171
55.4k
        }
172
65.9k
        return ret;
173
66.2k
    }
174
175
519k
next:
176
519k
    p_prev = m_el[mi_level];
177
519k
    if( p_prev && p_prev->IsFiniteSize() )
178
399k
        p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
179
180
519k
    uint64_t i_max_read;
181
519k
    if (mi_level == 0)
182
0
        i_max_read = UINT64_MAX;
183
519k
    else if (!m_el[mi_level-1]->IsFiniteSize())
184
16.6k
        i_max_read = UINT64_MAX;
185
502k
    else if (!p_prev)
186
110k
    {
187
110k
        i_max_read = m_el[mi_level-1]->GetSize();
188
110k
        if (i_max_read == 0)
189
147
        {
190
            /* check if the parent still has data to read */
191
147
            if ( mi_level > 1 && m_el[mi_level-2]->IsFiniteSize() &&
192
146
                 m_el[mi_level-1]->GetEndPosition() < m_el[mi_level-2]->GetEndPosition() )
193
144
            {
194
144
                uint64_t top = m_el[mi_level-2]->GetEndPosition();
195
144
                uint64_t bom = m_el[mi_level-1]->GetEndPosition();
196
144
                i_max_read = top - bom;
197
144
            }
198
147
        }
199
110k
    }
200
391k
    else {
201
391k
        size_t size_lvl = mi_level;
202
452k
        while ( size_lvl && m_el[size_lvl-1]->IsFiniteSize() && m_el[size_lvl]->IsFiniteSize() &&
203
444k
                m_el[size_lvl-1]->GetEndPosition() == m_el[size_lvl]->GetEndPosition() )
204
61.2k
            size_lvl--;
205
391k
        if (size_lvl == 0 && !allow_overshoot)
206
0
        {
207
0
            i_ulev = mi_level; // trick to go all the way up
208
0
            m_el[mi_level] = NULL;
209
0
            do_read = false;
210
0
        }
211
391k
        else if (size_lvl == 0 || !m_el[size_lvl-1]->IsFiniteSize() || !m_el[size_lvl]->IsFiniteSize() )
212
8.65k
            i_max_read = UINT64_MAX;
213
383k
        else {
214
383k
            uint64_t top = m_el[size_lvl-1]->GetEndPosition();
215
383k
            uint64_t bom = m_el[mi_level]->GetEndPosition();
216
383k
            i_max_read = top - bom;
217
383k
        }
218
391k
    }
219
220
519k
    if (do_read)
221
519k
    {
222
        // avoid leaking previous element at the level we're reading
223
519k
        if(m_el[mi_level] != nullptr && !mb_keep)
224
364k
        {
225
364k
            assert(m_el[mi_level] == p_prev);
226
364k
            delete m_el[mi_level];
227
364k
            p_prev = nullptr;
228
364k
        }
229
230
        // If the parent is a segment, use the segment context when creating children
231
        // (to prolong their lifetime), otherwise just continue as normal
232
519k
        EbmlSemanticContext e_context =
233
519k
                (mi_level == 0 || EBML_CTX_MASTER( EBML_CONTEXT(m_el[mi_level - 1]) ) == EBML_CTX_MASTER( Context_KaxSegmentVLC ))
234
519k
                ? Context_KaxSegmentVLC
235
519k
                : EBML_CONTEXT(m_el[mi_level - 1]);
236
237
        /* Ignore unknown level 0 or 1 elements */
238
519k
        m_el[mi_level] = unlikely(!i_max_read) ? NULL :
239
519k
                         m_es->FindNextElement( e_context,
240
519k
                                                i_ulev, i_max_read,
241
519k
                                                (  mb_dummy | (mi_level > 1) ), 1 );
242
243
519k
        if( m_el[mi_level] == NULL )
244
18.7k
        {
245
18.7k
            if ( i_max_read != UINT64_MAX && !m_es->I_O().IsEOF() )
246
16.2k
            {
247
16.2k
                msg_Dbg(p_demux, "found nothing, go up");
248
16.2k
                i_ulev = 1;
249
16.2k
            }
250
18.7k
        }
251
519k
    }
252
253
519k
    if( i_ulev > 0 )
254
81.4k
    {
255
81.4k
        if( p_prev )
256
14.2k
        {
257
14.2k
            if( !mb_keep )
258
0
            {
259
0
                delete p_prev;
260
0
                p_prev = NULL;
261
0
            }
262
14.2k
            mb_keep = false;
263
14.2k
        }
264
170k
        while( i_ulev > 0 )
265
90.7k
        {
266
90.7k
            if( mi_level == 1 )
267
1.48k
            {
268
1.48k
                mi_level = 0;
269
1.48k
                return NULL;
270
1.48k
            }
271
272
89.2k
            delete m_el[mi_level - 1];
273
89.2k
            m_el[mi_level -1] = m_el[mi_level];
274
89.2k
            m_el[mi_level] = NULL;
275
89.2k
            return_previous_parent = m_el[mi_level - 1] != NULL;
276
277
89.2k
            mi_level--;
278
89.2k
            i_ulev--;
279
89.2k
        }
280
80.0k
        return NULL;
281
81.4k
    }
282
437k
    if( m_el[mi_level] == NULL )
283
2.58k
    {
284
2.58k
        msg_Dbg( p_demux,"MKV/Ebml Parser: m_el[mi_level] == NULL" );
285
        /* go back to the end of the parent */
286
2.58k
        if( p_prev && p_prev->IsFiniteSize() )
287
7
            p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
288
2.58k
    }
289
434k
    else if( m_el[mi_level]->IsDummy() && !mb_dummy )
290
177k
    {
291
177k
        bool b_bad_position = false;
292
        /* We got a dummy element but don't want those...
293
         * perform a sanity check */
294
177k
        if( !mi_level )
295
0
        {
296
0
            msg_Err(p_demux, "Got invalid lvl 0 element... Aborting");
297
0
            return NULL;
298
0
        }
299
300
177k
        if( mi_level > 1 &&
301
177k
            p_prev && p_prev->IsFiniteSize() &&
302
3.30k
            p_prev->GetEndPosition() != m_el[mi_level]->GetElementPosition() )
303
1.81k
        {
304
1.81k
            msg_Err( p_demux, "Dummy Element at unexpected position (%" PRIu64 " instead of %" PRIu64 ")... corrupted file?",
305
1.81k
                        m_el[mi_level]->GetElementPosition(), p_prev->GetEndPosition() );
306
1.81k
            b_bad_position = true;
307
1.81k
        }
308
309
177k
        if( p_prev )
310
3.30k
        {
311
3.30k
            if( !mb_keep )
312
0
            {
313
0
                delete p_prev;
314
0
                p_prev = NULL;
315
0
            }
316
3.30k
            mb_keep = false;
317
3.30k
        }
318
319
177k
        if( n_call < M_EL_MAXSIZE && !b_bad_position && m_el[mi_level]->IsFiniteSize() &&
320
120k
            ( !m_el[mi_level-1]->IsFiniteSize() ||
321
118k
              m_el[mi_level]->GetEndPosition() <= m_el[mi_level-1]->GetEndPosition() ) )
322
118k
        {
323
            /* The element fits inside its upper element */
324
118k
            msg_Warn( p_demux, "Dummy element found %" PRIu64 "... skipping it",
325
118k
                      m_el[mi_level]->GetElementPosition() );
326
118k
            n_call++;
327
118k
        }
328
58.9k
        else
329
58.9k
        {
330
            /* Too large, misplaced or M_EL_MAXSIZE successive dummy elements */
331
58.9k
            msg_Err( p_demux,
332
58.9k
                     "Dummy element too large or misplaced at %" PRIu64 "... skipping to next upper element",
333
58.9k
                     m_el[mi_level]->GetElementPosition() );
334
335
58.9k
            if( m_el[mi_level]->IsFiniteSize() && m_el[mi_level-1]->IsFiniteSize() &&
336
58.8k
                m_el[mi_level]->GetElementPosition() >= m_el[mi_level-1]->GetEndPosition() )
337
3.05k
            {
338
3.05k
                msg_Err(p_demux, "This element is outside its known parent... upping level");
339
3.05k
                delete m_el[mi_level - 1];
340
3.05k
                m_el[mi_level -1] = m_el[mi_level];
341
3.05k
                m_el[mi_level] = NULL;
342
3.05k
                return_previous_parent = m_el[mi_level - 1] != NULL;
343
344
3.05k
                mi_level--;
345
3.05k
                return NULL;
346
3.05k
            }
347
58.9k
        }
348
349
174k
        goto next;
350
177k
    }
351
352
259k
    if( p_prev )
353
18.1k
    {
354
18.1k
        if( !mb_keep )
355
0
        {
356
0
            delete p_prev;
357
0
        }
358
18.1k
        mb_keep = false;
359
18.1k
    }
360
259k
    return m_el[mi_level];
361
437k
}
362
363
bool EbmlParser::IsTopPresent( EbmlElement *el ) const
364
152k
{
365
304k
    for( size_t i = 0; i < mi_level; i++ )
366
304k
    {
367
304k
        if( m_el[i] && m_el[i] == el )
368
152k
            return true;
369
304k
    }
370
0
    return false;
371
152k
}
372
373
} // namespace