Coverage Report

Created: 2026-11-12 07:28

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
8.00k
    p_demux( p_demux ),
35
8.00k
    m_es( es ),
36
8.00k
    mi_level( 1 ),
37
8.00k
    mi_user_level( 1 ),
38
8.00k
    mb_keep( false ),
39
8.00k
    mb_dummy( var_InheritBool( p_demux, "mkv-use-dummy" ) )
40
8.00k
{
41
8.00k
    memset( m_el, 0, sizeof( *m_el ) * M_EL_MAXSIZE);
42
8.00k
    m_el[0] = el_start;
43
8.00k
}
44
45
EbmlParser::~EbmlParser( void )
46
8.00k
{
47
8.00k
    if( !mi_level )
48
2.59k
    {
49
2.59k
        assert( !mb_keep );
50
2.59k
        delete m_el[1];
51
2.59k
        return;
52
2.59k
    }
53
54
13.8k
    for( size_t i = 1; i <= mi_level; i++ )
55
8.41k
    {
56
8.41k
        if( !mb_keep )
57
8.41k
        {
58
8.41k
            delete m_el[i];
59
8.41k
        }
60
8.41k
        mb_keep = false;
61
8.41k
    }
62
5.40k
}
63
64
void EbmlParser::reconstruct( matroska_iostream_c* es, EbmlElement* el_start, demux_t* p_demux )
65
3.89k
{
66
3.89k
    this->~EbmlParser();
67
68
3.89k
    new( static_cast<void*>( this ) ) EbmlParser( es, el_start, p_demux );
69
3.89k
}
70
71
void EbmlParser::Up( void )
72
91.4k
{
73
91.4k
    if( mi_user_level == mi_level && m_el[mi_level] )
74
0
    {
75
0
        msg_Warn( p_demux, "MKV/Ebml Parser: Up cannot escape itself" );
76
0
    }
77
78
91.4k
    assert(mi_user_level != 0);
79
91.4k
    mi_user_level--;
80
91.4k
}
81
82
void EbmlParser::Down( void )
83
94.8k
{
84
94.8k
    assert(m_el[mi_level] != nullptr);
85
94.8k
    mi_user_level++;
86
94.8k
    mi_level++;
87
94.8k
}
88
89
void EbmlParser::Keep( void )
90
42.7k
{
91
42.7k
    mb_keep = true;
92
42.7k
}
93
94
void EbmlParser::Unkeep()
95
2.12k
{
96
2.12k
    mb_keep = false;
97
2.12k
}
98
99
int EbmlParser::GetLevel( void ) const
100
351k
{
101
351k
    return mi_user_level;
102
351k
}
103
104
void EbmlParser::Reset( demux_t *p_demux )
105
2.59k
{
106
4.72k
    while ( mi_level > 0)
107
2.13k
    {
108
2.13k
        delete m_el[mi_level];
109
2.13k
        m_el[mi_level] = NULL;
110
2.13k
        mi_level--;
111
2.13k
    }
112
2.59k
    this->p_demux = p_demux;
113
2.59k
    mi_user_level = mi_level = 1;
114
2.59k
    return_previous_parent = false;
115
    // a little faster and cleaner
116
2.59k
    m_es->I_O().setFilePointer( static_cast<EbmlMaster*>(m_el[0])->GetDataStart() );
117
2.59k
}
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
471k
{
138
471k
    int i_ulev = 0;
139
471k
    int n_call = 0;
140
471k
    EbmlElement *p_prev = NULL;
141
471k
    bool do_read = true;
142
143
471k
    if( mi_user_level != mi_level )
144
45.8k
    {
145
45.8k
        return NULL;
146
45.8k
    }
147
425k
    if( return_previous_parent )
148
65.5k
    {
149
65.5k
        EbmlElement *ret = m_el[mi_level];
150
65.5k
        return_previous_parent = false;
151
152
65.5k
        if( mi_level > 0 && m_el[mi_level-1]->IsFiniteSize() )
153
54.5k
        {
154
54.5k
            if( ret->IsFiniteSize() &&
155
54.5k
                ret->GetEndPosition() > m_el[mi_level-1]->GetEndPosition() )
156
173
            {
157
173
                msg_Err( p_demux, "EBML element at %" PRIu64 " extends beyond parent boundary (%" PRIu64 " beyond %" PRIu64 ")",
158
173
                         ret->GetElementPosition(), ret->GetEndPosition(), m_el[mi_level-1]->GetEndPosition() );
159
173
                delete ret;
160
173
                m_el[mi_level] = NULL;
161
173
                return NULL;
162
173
            }
163
54.3k
            if( !ret->IsFiniteSize() )
164
3
            {
165
3
                msg_Dbg( p_demux, "Infinite EBML element %s at %" PRIu64 " inside finite parent",
166
3
                         EBML_NAME(ret), ret->GetElementPosition() );
167
3
            }
168
54.3k
        }
169
65.3k
        return ret;
170
65.5k
    }
171
172
598k
next:
173
598k
    p_prev = m_el[mi_level];
174
598k
    if( p_prev && p_prev->IsFiniteSize() )
175
475k
        p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
176
177
598k
    uint64_t i_max_read;
178
598k
    if (mi_level == 0)
179
0
        i_max_read = UINT64_MAX;
180
598k
    else if (!m_el[mi_level-1]->IsFiniteSize())
181
20.5k
        i_max_read = UINT64_MAX;
182
577k
    else if (!p_prev)
183
113k
    {
184
113k
        i_max_read = m_el[mi_level-1]->GetSize();
185
113k
        if (i_max_read == 0)
186
263
        {
187
            /* check if the parent still has data to read */
188
263
            if ( mi_level > 1 && m_el[mi_level-2]->IsFiniteSize() &&
189
263
                 m_el[mi_level-1]->GetEndPosition() < m_el[mi_level-2]->GetEndPosition() )
190
263
            {
191
263
                uint64_t top = m_el[mi_level-2]->GetEndPosition();
192
263
                uint64_t bom = m_el[mi_level-1]->GetEndPosition();
193
263
                i_max_read = top - bom;
194
263
            }
195
263
        }
196
113k
    }
197
464k
    else {
198
464k
        size_t size_lvl = mi_level;
199
515k
        while ( size_lvl && m_el[size_lvl-1]->IsFiniteSize() && m_el[size_lvl]->IsFiniteSize() &&
200
506k
                m_el[size_lvl-1]->GetEndPosition() == m_el[size_lvl]->GetEndPosition() )
201
51.1k
            size_lvl--;
202
464k
        if (size_lvl == 0 && !allow_overshoot)
203
0
        {
204
0
            i_ulev = mi_level; // trick to go all the way up
205
0
            m_el[mi_level] = NULL;
206
0
            do_read = false;
207
0
        }
208
464k
        else if (size_lvl == 0 || !m_el[size_lvl-1]->IsFiniteSize() || !m_el[size_lvl]->IsFiniteSize() )
209
9.68k
            i_max_read = UINT64_MAX;
210
455k
        else {
211
455k
            uint64_t top = m_el[size_lvl-1]->GetEndPosition();
212
455k
            uint64_t bom = m_el[mi_level]->GetEndPosition();
213
455k
            i_max_read = top - bom;
214
455k
        }
215
464k
    }
216
217
598k
    if (do_read)
218
598k
    {
219
        // avoid leaking previous element at the level we're reading
220
598k
        if(m_el[mi_level] != nullptr && !mb_keep)
221
432k
        {
222
432k
            assert(m_el[mi_level] == p_prev);
223
432k
            delete m_el[mi_level];
224
432k
            p_prev = nullptr;
225
432k
        }
226
227
        // If the parent is a segment, use the segment context when creating children
228
        // (to prolong their lifetime), otherwise just continue as normal
229
598k
        EbmlSemanticContext e_context =
230
598k
                (mi_level == 0 || EBML_CTX_MASTER( EBML_CONTEXT(m_el[mi_level - 1]) ) == EBML_CTX_MASTER( Context_KaxSegmentVLC ))
231
598k
                ? Context_KaxSegmentVLC
232
598k
                : EBML_CONTEXT(m_el[mi_level - 1]);
233
234
        /* Ignore unknown level 0 or 1 elements */
235
598k
        m_el[mi_level] = unlikely(!i_max_read) ? NULL :
236
598k
                         m_es->FindNextElement( e_context,
237
598k
                                                i_ulev, i_max_read,
238
598k
                                                (  mb_dummy | (mi_level > 1) ), 1 );
239
240
598k
        if( m_el[mi_level] == NULL )
241
22.4k
        {
242
22.4k
            if ( i_max_read != UINT64_MAX )
243
21.3k
            {
244
21.3k
                msg_Dbg(p_demux, "found nothing, go up");
245
21.3k
                i_ulev = 1;
246
21.3k
            }
247
22.4k
        }
248
598k
    }
249
250
598k
    if( i_ulev > 0 )
251
85.8k
    {
252
85.8k
        if( p_prev )
253
16.7k
        {
254
16.7k
            if( !mb_keep )
255
0
            {
256
0
                delete p_prev;
257
0
                p_prev = NULL;
258
0
            }
259
16.7k
            mb_keep = false;
260
16.7k
        }
261
173k
        while( i_ulev > 0 )
262
91.0k
        {
263
91.0k
            if( mi_level == 1 )
264
3.05k
            {
265
3.05k
                mi_level = 0;
266
3.05k
                return NULL;
267
3.05k
            }
268
269
87.9k
            delete m_el[mi_level - 1];
270
87.9k
            m_el[mi_level -1] = m_el[mi_level];
271
87.9k
            m_el[mi_level] = NULL;
272
87.9k
            return_previous_parent = m_el[mi_level - 1] != NULL;
273
274
87.9k
            mi_level--;
275
87.9k
            i_ulev--;
276
87.9k
        }
277
82.8k
        return NULL;
278
85.8k
    }
279
512k
    if( m_el[mi_level] == NULL )
280
1.05k
    {
281
1.05k
        msg_Dbg( p_demux,"MKV/Ebml Parser: m_el[mi_level] == NULL" );
282
        /* go back to the end of the parent */
283
1.05k
        if( p_prev && p_prev->IsFiniteSize() )
284
0
            p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
285
1.05k
    }
286
511k
    else if( m_el[mi_level]->IsDummy() && !mb_dummy )
287
242k
    {
288
242k
        bool b_bad_position = false;
289
        /* We got a dummy element but don't want those...
290
         * perform a sanity check */
291
242k
        if( !mi_level )
292
0
        {
293
0
            msg_Err(p_demux, "Got invalid lvl 0 element... Aborting");
294
0
            return NULL;
295
0
        }
296
297
242k
        if( mi_level > 1 &&
298
242k
            p_prev && p_prev->IsFiniteSize() &&
299
4.03k
            p_prev->GetEndPosition() != m_el[mi_level]->GetElementPosition() )
300
1.74k
        {
301
1.74k
            msg_Err( p_demux, "Dummy Element at unexpected position (%" PRIu64 " instead of %" PRIu64 ")... corrupted file?",
302
1.74k
                        m_el[mi_level]->GetElementPosition(), p_prev->GetEndPosition() );
303
1.74k
            b_bad_position = true;
304
1.74k
        }
305
306
242k
        if( p_prev )
307
4.03k
        {
308
4.03k
            if( !mb_keep )
309
0
            {
310
0
                delete p_prev;
311
0
                p_prev = NULL;
312
0
            }
313
4.03k
            mb_keep = false;
314
4.03k
        }
315
316
242k
        if( n_call < M_EL_MAXSIZE && !b_bad_position && m_el[mi_level]->IsFiniteSize() &&
317
176k
            ( !m_el[mi_level-1]->IsFiniteSize() ||
318
173k
              m_el[mi_level]->GetEndPosition() <= m_el[mi_level-1]->GetEndPosition() ) )
319
173k
        {
320
            /* The element fits inside its upper element */
321
173k
            msg_Warn( p_demux, "Dummy element found %" PRIu64 "... skipping it",
322
173k
                      m_el[mi_level]->GetElementPosition() );
323
173k
            n_call++;
324
173k
        }
325
68.9k
        else
326
68.9k
        {
327
            /* Too large, misplaced or M_EL_MAXSIZE successive dummy elements */
328
68.9k
            msg_Err( p_demux,
329
68.9k
                     "Dummy element too large or misplaced at %" PRIu64 "... skipping to next upper element",
330
68.9k
                     m_el[mi_level]->GetElementPosition() );
331
332
68.9k
            if( m_el[mi_level]->IsFiniteSize() && m_el[mi_level-1]->IsFiniteSize() &&
333
68.8k
                m_el[mi_level]->GetElementPosition() >= m_el[mi_level-1]->GetEndPosition() )
334
3.84k
            {
335
3.84k
                msg_Err(p_demux, "This element is outside its known parent... upping level");
336
3.84k
                delete m_el[mi_level - 1];
337
3.84k
                m_el[mi_level -1] = m_el[mi_level];
338
3.84k
                m_el[mi_level] = NULL;
339
3.84k
                return_previous_parent = m_el[mi_level - 1] != NULL;
340
341
3.84k
                mi_level--;
342
3.84k
                return NULL;
343
3.84k
            }
344
68.9k
        }
345
346
238k
        goto next;
347
242k
    }
348
349
269k
    if( p_prev )
350
21.9k
    {
351
21.9k
        if( !mb_keep )
352
0
        {
353
0
            delete p_prev;
354
0
        }
355
21.9k
        mb_keep = false;
356
21.9k
    }
357
269k
    return m_el[mi_level];
358
512k
}
359
360
bool EbmlParser::IsTopPresent( EbmlElement *el ) const
361
200k
{
362
401k
    for( size_t i = 0; i < mi_level; i++ )
363
401k
    {
364
401k
        if( m_el[i] && m_el[i] == el )
365
200k
            return true;
366
401k
    }
367
0
    return false;
368
200k
}
369
370
} // namespace