AVIファイルには複数のトラックを含めることができます。例えば、オーディオトラックとビデオトラックです。
任意の数のトラックを解析するために、VLCはまず特定のトラックが完成して再生可能かどうかを判定する必要があります。この目的のために2つの変数が設定されています。どちらもi_trackという名前です。
ここでは、p_sys変数がAVIファイルを表します。したがって、完成して再生可能と判定されたトラックを参照するために使用される「正式な」i_track変数はp_sys->i_trackです。これは次のマクロで初期化されます:
```
TAB_INIT(p_sys->i_track, p_sys->track);
//#define TAB_INIT( count, tab ) do {(count) = 0; (tab) = NULL; } while(0)
```
一方、2番目のi_track変数は、完成しているか再生可能かどうかに関係なく、すべてのトラックを数えるために設定されます。
```
i_track = AVI_ChunkCount( p_hdrl, AVIFOURCC_strl, true );
```
2番目のi_trackは、'strl'で始まるすべてのチャンクを表します。これは、一連のチェックを通過した場合にのみp_sys->i_track変数を設定する 'for' ループを制御します:
```
for( unsigned i = 0 ; i < i_track; i++ )
{
...
TAB_APPEND( p_sys->i_track, p_sys->track, tk );
//#define TAB_APPEND( count, tab, p ) {TAB_APPEND_CAST( , count, tab, p )}
//TAB_APPEND_CAST( ,p_sys->i_track, p_sys->track, tk)
//if( (count) > 0 ) (tab) = cast realloc( tab, sizeof( *(tab) ) * ( (count) + 1 ) );
//else (tab) = cast malloc( sizeof( *(tab) ) );
//if( !(tab) ) abort();
//(tab)[count] = (p);
//(count)++; //p_sys->i_track incremented here
}
```
後続の 'for' ループでは、p_sys->i_track変数がAVI_ChunkFindの呼び出しでチャンク番号として使用されます。AVI_ChunkFindは、指定されたFOURCCチャンク値(この場合は'strl')に一致するn番目のチャンクを返します:
```
for( unsigned i = 0 ; i < p_sys->i_track; i++ )
{
avi_track_t *tk = p_sys->track[i];
if( tk->fmt.i_cat != AUDIO_ES ||
tk->idx.i_size < 1 ||
tk->i_scale != 1 ||
tk->i_samplesize != 0 )
continue;
//CVE-2021-25804
avi_chunk_list_t *p_strl = AVI_ChunkFind( p_hdrl, AVIFOURCC_strl, i, true );
//subsequently changed to: avi_chunk_list_t *p_strl = AVI_ChunkFind( p_hdrl, AVIFOURCC_strl, tk->fmt.i_id, true );
avi_chunk_strf_t *p_strf = AVI_ChunkFind( p_strl, AVIFOURCC_strf, 0, false );
if( !p_strf || p_strf->i_cat != AUDIO_ES )
continue;
const WAVEFORMATEX *p_wf = p_strf->u.p_wf;
```
p_sys->i_trackは完成したトラックの正しい数を数えますが、それがどのチャンク番号に設定されたかは追跡しません。つまり、有効なstrlチャンクの前に無効なstrlチャンクがある場合、トラックオブジェクトの設定時には最初のチャンクが正しく無視されますが、ChunkFindが呼び出されると、依然として最初の無効なチャンクが選択されます。
CVE-2021-25804が作成された後、ChunkFindのチャンク番号変数をiからtk->fmt.i_idに変更することで問題は修正されました。tk->fmt.i_idは、先ほど確認したTAB_APPEND()呼び出しのすぐ上で最初に設定されます:
```
tk->fmt.i_id = i;
if( p_strn && p_strn->p_str )
tk->fmt.psz_description = FromACP( p_strn->p_str );
//see es_format_truncated.c
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
TAB_APPEND( p_sys->i_track, p_sys->track, tk );
```
これで問題が修正されているように見えます。tk->fmt.i_idがiに設定されるため、完成したチャンクがChunkFind()によって選択されるはずです。しかし、テストにより、問題のChunkFind()呼び出しの時点では、実際にはtk->fmt.i_idの値が-1に設定されていることが明らかになりました。これはes_format_Init()の呼び出し中に設定される初期化値です。
```
void es_format_Init( es_format_t *fmt,
int i_cat, vlc_fourcc_t i_codec )
{
memset(fmt, 0, sizeof (*fmt));
fmt->i_cat = i_cat;
fmt->i_codec = i_codec;
fmt->i_profile = -1;
fmt->i_level = -1;
fmt->i_id = -1;
fmt->i_priority = ES_PRIORITY_SELECTABLE_MIN;
fmt->psz_language = NULL;
fmt->psz_description = NULL;
fmt->p_extra_languages = NULL;
if (fmt->i_cat == VIDEO_ES)
video_format_Init(&fmt->video, 0);
fmt->b_packetized = true;
fmt->p_extra = NULL;
}
```
この初期化は、fmt->i_idがiに設定されるのと同じ 'for' ループ内で発生します。
```
for( unsigned i = 0 ; i < i_track; i++ )
{
...
switch( p_strh->i_type )
{
case( AVIFOURCC_auds ):
{
es_format_Init( &tk->fmt, AUDIO_ES, 0 );
...
}
...
}
tk->fmt.i_id = i;
if( p_strn && p_strn->p_str )
tk->fmt.psz_description = FromACP( p_strn->p_str );
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
TAB_APPEND( p_sys->i_track, p_sys->track, tk );
}
```