Coverage Report

Created: 2026-01-10 07:00

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
11.9k
    p_demux( p_demux ),
35
11.9k
    m_es( es ),
36
11.9k
    mi_level( 1 ),
37
11.9k
    m_got( NULL ),
38
11.9k
    mi_user_level( 1 ),
39
11.9k
    mb_keep( false ),
40
11.9k
    mb_dummy( var_InheritBool( p_demux, "mkv-use-dummy" ) )
41
11.9k
{
42
11.9k
    memset( m_el, 0, sizeof( *m_el ) * M_EL_MAXSIZE);
43
11.9k
    m_el[0] = el_start;
44
11.9k
}
45
46
EbmlParser::~EbmlParser( void )
47
11.9k
{
48
11.9k
    if( !mi_level )
49
4.86k
    {
50
4.86k
        assert( !mb_keep );
51
4.86k
        delete m_el[1];
52
4.86k
        return;
53
4.86k
    }
54
55
16.4k
    for( int i = 1; i <= mi_level; i++ )
56
9.36k
    {
57
9.36k
        if( !mb_keep )
58
9.36k
        {
59
9.36k
            delete m_el[i];
60
9.36k
        }
61
9.36k
        mb_keep = false;
62
9.36k
    }
63
7.07k
}
64
65
void EbmlParser::reconstruct( matroska_iostream_c* es, EbmlElement* el_start, demux_t* p_demux )
66
2.27k
{
67
2.27k
    this->~EbmlParser();
68
69
2.27k
    new( static_cast<void*>( this ) ) EbmlParser( es, el_start, p_demux );
70
2.27k
}
71
72
void EbmlParser::Up( void )
73
147k
{
74
147k
    if( mi_user_level == mi_level && m_el[mi_level] )
75
56
    {
76
56
        msg_Warn( p_demux, "MKV/Ebml Parser: Up cannot escape itself" );
77
56
    }
78
79
147k
    mi_user_level--;
80
147k
}
81
82
void EbmlParser::Down( void )
83
148k
{
84
148k
    mi_user_level++;
85
148k
    mi_level++;
86
148k
}
87
88
void EbmlParser::Keep( void )
89
11.5k
{
90
11.5k
    mb_keep = true;
91
11.5k
}
92
93
void EbmlParser::Unkeep()
94
5.28k
{
95
5.28k
    mb_keep = false;
96
5.28k
}
97
98
int EbmlParser::GetLevel( void ) const
99
129k
{
100
129k
    return mi_user_level;
101
129k
}
102
103
void EbmlParser::Reset( demux_t *p_demux )
104
5.10k
{
105
9.97k
    while ( mi_level > 0)
106
4.86k
    {
107
4.86k
        delete m_el[mi_level];
108
4.86k
        m_el[mi_level] = NULL;
109
4.86k
        mi_level--;
110
4.86k
    }
111
5.10k
    this->p_demux = p_demux;
112
5.10k
    mi_user_level = mi_level = 1;
113
    // a little faster and cleaner
114
5.10k
    m_es->I_O().setFilePointer( static_cast<EbmlMaster*>(m_el[0])->GetDataStart() );
115
5.10k
}
116
117
118
static const EbmlSemanticContext & GetEbmlNoGlobal_Context();
119
static const EbmlSemanticContext EbmlNoGlobal_Context = EbmlSemanticContext(0, NULL, NULL, *GetEbmlNoGlobal_Context, NULL);
120
static const EbmlSemanticContext & GetEbmlNoGlobal_Context()
121
0
{
122
0
  return EbmlNoGlobal_Context;
123
0
}
124
125
// the Segment Context should not allow Void or CRC32 elements to avoid lookup false alarm
126
DEFINE_START_SEMANTIC(KaxSegmentVLC)
127
DEFINE_SEMANTIC_ITEM(true, true, EbmlHead)
128
DEFINE_SEMANTIC_ITEM(false, false, KaxSegment)
129
DEFINE_END_SEMANTIC(KaxSegmentVLC)
130
131
DEFINE_xxx_CONTEXT(KaxSegmentVLC,GetEbmlNoGlobal_Context)
132
133
134
EbmlElement *EbmlParser::Get( bool allow_overshoot )
135
639k
{
136
639k
    int i_ulev = 0;
137
639k
    int n_call = 0;
138
639k
    EbmlElement *p_prev = NULL;
139
639k
    bool do_read = true;
140
141
639k
    if( mi_user_level != mi_level )
142
46.8k
    {
143
46.8k
        return NULL;
144
46.8k
    }
145
593k
    if( m_got )
146
96.2k
    {
147
96.2k
        EbmlElement *ret = m_got;
148
96.2k
        m_got = NULL;
149
150
96.2k
        return ret;
151
96.2k
    }
152
153
557k
next:
154
557k
    p_prev = m_el[mi_level];
155
557k
    if( p_prev )
156
381k
        p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
157
158
557k
    uint64_t i_max_read;
159
557k
    if (mi_level == 0)
160
0
        i_max_read = UINT64_MAX;
161
557k
    else if (!m_el[mi_level-1]->IsFiniteSize())
162
9.07k
        i_max_read = UINT64_MAX;
163
548k
    else if (!p_prev)
164
172k
    {
165
172k
        i_max_read = m_el[mi_level-1]->GetSize();
166
172k
        if (i_max_read == 0)
167
828
        {
168
            /* check if the parent still has data to read */
169
828
            if ( mi_level > 1 && m_el[mi_level-2]->IsFiniteSize() &&
170
787
                 m_el[mi_level-1]->GetEndPosition() < m_el[mi_level-2]->GetEndPosition() )
171
336
            {
172
336
                uint64_t top = m_el[mi_level-2]->GetEndPosition();
173
336
                uint64_t bom = m_el[mi_level-1]->GetEndPosition();
174
336
                i_max_read = top - bom;
175
336
            }
176
828
        }
177
172k
    }
178
375k
    else {
179
375k
        size_t size_lvl = mi_level;
180
486k
        while ( size_lvl && m_el[size_lvl-1]->IsFiniteSize() && m_el[size_lvl]->IsFiniteSize() &&
181
483k
                m_el[size_lvl-1]->GetEndPosition() == m_el[size_lvl]->GetEndPosition() )
182
111k
            size_lvl--;
183
375k
        if (size_lvl == 0 && !allow_overshoot)
184
0
        {
185
0
            i_ulev = mi_level; // trick to go all the way up
186
0
            m_el[mi_level] = NULL;
187
0
            do_read = false;
188
0
        }
189
375k
        else if (size_lvl == 0 || !m_el[size_lvl-1]->IsFiniteSize() || !m_el[size_lvl]->IsFiniteSize() )
190
2.99k
            i_max_read = UINT64_MAX;
191
372k
        else {
192
372k
            uint64_t top = m_el[size_lvl-1]->GetEndPosition();
193
372k
            uint64_t bom = m_el[mi_level]->GetEndPosition();
194
372k
            i_max_read = top - bom;
195
372k
        }
196
375k
    }
197
198
557k
    if (do_read)
199
557k
    {
200
        // avoid leaking previous element at the level we're reading
201
557k
        if(m_el[mi_level] != nullptr && !mb_keep)
202
370k
        {
203
370k
            assert(m_el[mi_level] == p_prev);
204
370k
            delete m_el[mi_level];
205
370k
            p_prev = nullptr;
206
370k
        }
207
208
        // If the parent is a segment, use the segment context when creating children
209
        // (to prolong their lifetime), otherwise just continue as normal
210
557k
        EbmlSemanticContext e_context =
211
557k
                EBML_CTX_MASTER( EBML_CONTEXT(m_el[mi_level - 1]) ) == EBML_CTX_MASTER( Context_KaxSegmentVLC )
212
557k
                ? Context_KaxSegmentVLC
213
557k
                : EBML_CONTEXT(m_el[mi_level - 1]);
214
215
        /* Ignore unknown level 0 or 1 elements */
216
557k
        m_el[mi_level] = unlikely(!i_max_read) ? NULL :
217
557k
                         m_es->FindNextElement( e_context,
218
556k
                                                i_ulev, i_max_read,
219
556k
                                                (  mb_dummy | (mi_level > 1) ), 1 );
220
221
557k
        if( m_el[mi_level] == NULL )
222
21.7k
        {
223
21.7k
            if ( i_max_read != UINT64_MAX && !m_es->I_O().IsEOF() )
224
15.3k
            {
225
15.3k
                msg_Dbg(p_demux, "found nothing, go up");
226
15.3k
                i_ulev = 1;
227
15.3k
            }
228
21.7k
        }
229
557k
    }
230
231
557k
    if( i_ulev > 0 )
232
110k
    {
233
110k
        if( p_prev )
234
3.45k
        {
235
3.45k
            if( !mb_keep )
236
0
            {
237
0
                delete p_prev;
238
0
                p_prev = NULL;
239
0
            }
240
3.45k
            mb_keep = false;
241
3.45k
        }
242
252k
        while( i_ulev > 0 )
243
146k
        {
244
146k
            if( mi_level == 1 )
245
5.09k
            {
246
5.09k
                mi_level = 0;
247
5.09k
                return NULL;
248
5.09k
            }
249
250
141k
            delete m_el[mi_level - 1];
251
141k
            m_got = m_el[mi_level -1] = m_el[mi_level];
252
141k
            m_el[mi_level] = NULL;
253
254
141k
            mi_level--;
255
141k
            i_ulev--;
256
141k
        }
257
105k
        return NULL;
258
110k
    }
259
446k
    else if( m_el[mi_level] == NULL )
260
6.40k
    {
261
6.40k
        msg_Dbg( p_demux,"MKV/Ebml Parser: m_el[mi_level] == NULL" );
262
        /* go back to the end of the parent */
263
6.40k
        if( p_prev )
264
21
            p_prev->SkipData( *m_es, EBML_CONTEXT(p_prev) );
265
6.40k
    }
266
440k
    else if( m_el[mi_level]->IsDummy() && !mb_dummy )
267
64.7k
    {
268
64.7k
        bool b_bad_position = false;
269
        /* We got a dummy element but don't want those...
270
         * perform a sanity check */
271
64.7k
        if( !mi_level )
272
0
        {
273
0
            msg_Err(p_demux, "Got invalid lvl 0 element... Aborting");
274
0
            return NULL;
275
0
        }
276
277
64.7k
        if( mi_level > 1 &&
278
64.7k
            p_prev && p_prev->IsFiniteSize() &&
279
358
            p_prev->GetEndPosition() != m_el[mi_level]->GetElementPosition() )
280
171
        {
281
171
            msg_Err( p_demux, "Dummy Element at unexpected position... corrupted file?" );
282
171
            b_bad_position = true;
283
171
        }
284
285
64.7k
        if( n_call < M_EL_MAXSIZE && !b_bad_position && m_el[mi_level]->IsFiniteSize() &&
286
36.1k
            ( !m_el[mi_level-1]->IsFiniteSize() ||
287
32.4k
              m_el[mi_level]->GetEndPosition() <= m_el[mi_level-1]->GetEndPosition() ) )
288
31.9k
        {
289
            /* The element fits inside its upper element */
290
31.9k
            msg_Warn( p_demux, "Dummy element found %" PRIu64 "... skipping it",
291
31.9k
                      m_el[mi_level]->GetElementPosition() );
292
31.9k
            if( p_prev )
293
74
            {
294
74
                if( !mb_keep )
295
0
                {
296
0
                    delete p_prev;
297
0
                    p_prev = NULL;
298
0
                }
299
74
                mb_keep = false;
300
74
            }
301
31.9k
            n_call++;
302
31.9k
            goto next;
303
31.9k
        }
304
32.8k
        else
305
32.8k
        {
306
            /* Too large, misplaced or M_EL_MAXSIZE successive dummy elements */
307
32.8k
            msg_Err( p_demux,
308
32.8k
                     "Dummy element too large or misplaced at %" PRIu64 "... skipping to next upper element",
309
32.8k
                     m_el[mi_level]->GetElementPosition() );
310
311
32.8k
            if( mi_level >= 1 &&
312
32.8k
                m_el[mi_level]->IsFiniteSize() && m_el[mi_level-1]->IsFiniteSize() &&
313
32.4k
                m_el[mi_level]->GetElementPosition() >= m_el[mi_level-1]->GetEndPosition() )
314
4.33k
            {
315
4.33k
                msg_Err(p_demux, "This element is outside its known parent... upping level");
316
4.33k
                delete m_el[mi_level - 1];
317
4.33k
                m_got = m_el[mi_level -1] = m_el[mi_level];
318
4.33k
                m_el[mi_level] = NULL;
319
320
4.33k
                mi_level--;
321
4.33k
                return NULL;
322
4.33k
            }
323
324
28.4k
            if( p_prev )
325
110
            {
326
110
                if( !mb_keep )
327
0
                {
328
0
                    delete p_prev;
329
0
                    p_prev = NULL;
330
0
                }
331
110
                mb_keep = false;
332
110
            }
333
28.4k
            goto next;
334
32.8k
        }
335
64.7k
    }
336
337
382k
    if( p_prev )
338
7.85k
    {
339
7.85k
        if( !mb_keep )
340
0
        {
341
0
            delete p_prev;
342
0
        }
343
7.85k
        mb_keep = false;
344
7.85k
    }
345
382k
    return m_el[mi_level];
346
557k
}
347
348
bool EbmlParser::IsTopPresent( EbmlElement *el ) const
349
91.5k
{
350
183k
    for( int i = 0; i < mi_level; i++ )
351
183k
    {
352
183k
        if( m_el[i] && m_el[i] == el )
353
91.5k
            return true;
354
183k
    }
355
0
    return false;
356
91.5k
}
357
358
} // namespace