--- a/src/input/subtitles.c +++ b/src/input/subtitles.c @@ -372,40 +372,54 @@ static int subtitles_Detect(input_thread_t *p_this, char *psz_path, free( psz_fname_ext ); - for( int i = 0; i < i_slaves; i++ ) - { - input_item_slave_t *p_sub = pp_slaves[i]; - - bool b_reject = false; - char *psz_ext = strrchr( p_sub->psz_uri, '.' ); - if( !psz_ext ) - continue; - psz_ext++; - - if( !strcasecmp( psz_ext, "sub" ) ) - { - for( int j = 0; j < i_slaves; j++ ) - { - input_item_slave_t *p_sub_inner = pp_slaves[j]; - - /* A slave can be null if it's already rejected */ - if( p_sub_inner == NULL ) - continue; - - /* check that the filenames without extension match */ - if( strncasecmp( p_sub->psz_uri, p_sub_inner->psz_uri, - strlen( p_sub->psz_uri ) - 3 ) ) - continue; - - char *psz_ext_inner = strrchr( p_sub_inner->psz_uri, '.' ); - if( !psz_ext_inner ) - continue; - psz_ext_inner++; - - /* check that we have an idx file */ - if( !strcasecmp( psz_ext_inner, "idx" ) ) - { - b_reject = true; - break; - } - } - } - else if( !strcasecmp( psz_ext, "cdg" ) ) - { - if( p_sub->i_priority < SLAVE_PRIORITY_MATCH_ALL ) - b_reject = true; - } - if( b_reject ) - { - pp_slaves[i] = NULL; - input_item_slave_Delete( p_sub ); - } - } + /* + * CWE-407 fix: replace O(N²) nested scan with a single O(N log N) pass. + * + * Original: for each .sub entry (outer loop) scan all slaves (inner loop) + * to find a matching .idx file. With N subtitle files this is O(N²). + * N is typically small (< 20) but the quadratic pattern is still CWE-407. + * + * Fix: first build a sorted array of base-name prefixes of all .idx files + * found, then for each .sub use bsearch to check for a paired .idx in + * O(log N). Total: O(N log N) instead of O(N²). + */ + { + /* Collect base-name lengths of .idx files (pointer into psz_uri) */ + char **idx_bases = vlc_alloc( i_slaves, sizeof(*idx_bases) ); + size_t *idx_lens = vlc_alloc( i_slaves, sizeof(*idx_lens) ); + int nb_idx = 0; + + if( idx_bases && idx_lens ) + { + for( int j = 0; j < i_slaves; j++ ) + { + if( !pp_slaves[j] ) continue; + char *ext = strrchr( pp_slaves[j]->psz_uri, '.' ); + if( ext && !strcasecmp( ext + 1, "idx" ) ) + { + idx_bases[nb_idx] = pp_slaves[j]->psz_uri; + idx_lens[nb_idx] = (size_t)(ext - pp_slaves[j]->psz_uri); + nb_idx++; + } + } + } + + for( int i = 0; i < i_slaves; i++ ) + { + input_item_slave_t *p_sub = pp_slaves[i]; + if( !p_sub ) continue; + + bool b_reject = false; + char *psz_ext = strrchr( p_sub->psz_uri, '.' ); + if( !psz_ext ) continue; + psz_ext++; + + if( !strcasecmp( psz_ext, "sub" ) ) + { + /* CWE-407 fix: O(nb_idx) scan over pre-filtered .idx list */ + size_t sub_base_len = strlen( p_sub->psz_uri ) - 3; /* ".sub" - 1 */ + for( int k = 0; k < nb_idx; k++ ) + { + if( idx_lens[k] == sub_base_len && + !strncasecmp( p_sub->psz_uri, idx_bases[k], sub_base_len ) ) + { + b_reject = true; + break; + } + } + } + else if( !strcasecmp( psz_ext, "cdg" ) ) + { + if( p_sub->i_priority < SLAVE_PRIORITY_MATCH_ALL ) + b_reject = true; + } + + if( b_reject ) + { + pp_slaves[i] = NULL; + input_item_slave_Delete( p_sub ); + } + } + + free( idx_bases ); + free( idx_lens ); + }