@@ -275,14 +275,12 @@ VkResult parse_generic_filter_environment_var(const struct loader_instance *inst
275275 const char * actual_start ;
276276 size_t actual_len ;
277277 determine_filter_type (token , & cur_filter_type , & actual_start , & actual_len );
278- if (actual_len > VK_MAX_EXTENSION_NAME_SIZE ) {
279- loader_strncpy (filter_struct -> filters [filter_struct -> count ].value , VK_MAX_EXTENSION_NAME_SIZE , actual_start ,
280- VK_MAX_EXTENSION_NAME_SIZE );
281- } else {
282- loader_strncpy (filter_struct -> filters [filter_struct -> count ].value , VK_MAX_EXTENSION_NAME_SIZE , actual_start ,
283- actual_len );
284- }
285- filter_struct -> filters [filter_struct -> count ].length = actual_len ;
278+ // value holds at most VK_MAX_EXTENSION_NAME_SIZE - 1 chars plus the null terminator. Keep the stored length in sync
279+ // with what actually fits so the matcher never walks past the buffer, and make sure it stays null terminated.
280+ size_t stored_len = actual_len < VK_MAX_EXTENSION_NAME_SIZE - 1 ? actual_len : VK_MAX_EXTENSION_NAME_SIZE - 1 ;
281+ loader_strncpy (filter_struct -> filters [filter_struct -> count ].value , VK_MAX_EXTENSION_NAME_SIZE , actual_start , stored_len );
282+ filter_struct -> filters [filter_struct -> count ].value [stored_len ] = '\0' ;
283+ filter_struct -> filters [filter_struct -> count ].length = stored_len ;
286284 filter_struct -> filters [filter_struct -> count ++ ].type = cur_filter_type ;
287285 if (filter_struct -> count >= MAX_ADDITIONAL_FILTERS ) {
288286 break ;
@@ -346,14 +344,13 @@ VkResult parse_layers_disable_filter_environment_var(const struct loader_instanc
346344 disable_struct -> disable_all_explicit = true;
347345 }
348346 } else {
349- if (actual_len > VK_MAX_EXTENSION_NAME_SIZE ) {
350- loader_strncpy (disable_struct -> additional_filters .filters [cur_count ].value , VK_MAX_EXTENSION_NAME_SIZE ,
351- actual_start , VK_MAX_EXTENSION_NAME_SIZE );
352- } else {
353- loader_strncpy (disable_struct -> additional_filters .filters [cur_count ].value , VK_MAX_EXTENSION_NAME_SIZE ,
354- actual_start , actual_len );
355- }
356- disable_struct -> additional_filters .filters [cur_count ].length = actual_len ;
347+ // Keep the stored length in sync with what actually fits (value is VK_MAX_EXTENSION_NAME_SIZE bytes including
348+ // the null terminator) so the matcher never reads past the buffer.
349+ size_t stored_len = actual_len < VK_MAX_EXTENSION_NAME_SIZE - 1 ? actual_len : VK_MAX_EXTENSION_NAME_SIZE - 1 ;
350+ loader_strncpy (disable_struct -> additional_filters .filters [cur_count ].value , VK_MAX_EXTENSION_NAME_SIZE , actual_start ,
351+ stored_len );
352+ disable_struct -> additional_filters .filters [cur_count ].value [stored_len ] = '\0' ;
353+ disable_struct -> additional_filters .filters [cur_count ].length = stored_len ;
357354 disable_struct -> additional_filters .filters [cur_count ].type = cur_filter_type ;
358355 disable_struct -> additional_filters .count ++ ;
359356 if (disable_struct -> additional_filters .count >= MAX_ADDITIONAL_FILTERS ) {
@@ -385,6 +382,18 @@ VkResult parse_layer_environment_var_filters(const struct loader_instance *inst,
385382 return res ;
386383}
387384
385+ // Case-insensitive compare of `count` bytes of a name against a filter value. Filter values are already lowercased when
386+ // they get parsed (see parse_generic_filter_environment_var), so we only need to fold the name side as we go. The caller
387+ // guarantees both sides have at least `count` valid bytes.
388+ static bool name_segment_matches_filter_value (const char * name_segment , const char * lowercase_filter_value , size_t count ) {
389+ for (size_t iii = 0 ; iii < count ; ++ iii ) {
390+ if ((char )tolower ((unsigned char )name_segment [iii ]) != lowercase_filter_value [iii ]) {
391+ return false;
392+ }
393+ }
394+ return true;
395+ }
396+
388397// Check to see if the provided layer name matches any of the filter strings.
389398// This will properly check against:
390399// - substrings "*string*"
@@ -394,54 +403,44 @@ VkResult parse_layer_environment_var_filters(const struct loader_instance *inst,
394403bool check_name_matches_filter_environment_var (const char * name , const struct loader_envvar_filter * filter_struct ) {
395404 bool ret_value = false;
396405 size_t name_len = strlen (name );
397- // name may be a manifest filename (from the driver select/disable filters) which, unlike layer names, is not capped to
398- // VK_MAX_EXTENSION_NAME_SIZE at parse time. Clamp so the lowercase copy stays inside lower_name.
399- if (name_len >= VK_MAX_EXTENSION_NAME_SIZE ) {
400- name_len = VK_MAX_EXTENSION_NAME_SIZE - 1 ;
401- }
402- char lower_name [VK_MAX_EXTENSION_NAME_SIZE ];
403- for (uint32_t iii = 0 ; iii < name_len ; ++ iii ) {
404- lower_name [iii ] = (char )tolower ((unsigned char )name [iii ]);
405- }
406- lower_name [name_len ] = '\0' ;
406+ // Compare each filter against `name` directly. name_segment_matches_filter_value does a case-insensitive compare
407+ // (filter values are already lowercased at parse time), so there's no need to make a lowercased copy of name first
408+ // and no limit on how long name can be.
407409 for (uint32_t filt = 0 ; filt < filter_struct -> count ; ++ filt ) {
408- // Check if the filter name is longer (this is with all special characters removed), and if it is
409- // continue since it can't match.
410- if (filter_struct -> filters [ filt ]. length > name_len ) {
410+ const struct loader_envvar_filter_value * filter = & filter_struct -> filters [ filt ];
411+ // The filter (with its wildcards stripped) is longer than the name, so it can't possibly match.
412+ if (filter -> length > name_len ) {
411413 continue ;
412414 }
413- switch (filter_struct -> filters [ filt ]. type ) {
415+ switch (filter -> type ) {
414416 case FILTER_STRING_SPECIAL :
415- if (!strcmp (VK_LOADER_DISABLE_ALL_LAYERS_VAR_1 , filter_struct -> filters [ filt ]. value ) ||
416- !strcmp (VK_LOADER_DISABLE_ALL_LAYERS_VAR_2 , filter_struct -> filters [ filt ]. value ) ||
417- !strcmp (VK_LOADER_DISABLE_ALL_LAYERS_VAR_3 , filter_struct -> filters [ filt ]. value )) {
417+ if (!strcmp (VK_LOADER_DISABLE_ALL_LAYERS_VAR_1 , filter -> value ) ||
418+ !strcmp (VK_LOADER_DISABLE_ALL_LAYERS_VAR_2 , filter -> value ) ||
419+ !strcmp (VK_LOADER_DISABLE_ALL_LAYERS_VAR_3 , filter -> value )) {
418420 ret_value = true;
419421 }
420422 break ;
421423
422424 case FILTER_STRING_SUBSTRING :
423- if (NULL != strstr (lower_name , filter_struct -> filters [filt ].value )) {
424- ret_value = true;
425+ // Slide the filter along the name looking for a match, stopping as soon as one is found.
426+ for (size_t start = 0 ; start + filter -> length <= name_len ; ++ start ) {
427+ if (name_segment_matches_filter_value (name + start , filter -> value , filter -> length )) {
428+ ret_value = true;
429+ break ;
430+ }
425431 }
426432 break ;
427433
428434 case FILTER_STRING_SUFFIX :
429- if (0 == strncmp (lower_name + name_len - filter_struct -> filters [filt ].length , filter_struct -> filters [filt ].value ,
430- filter_struct -> filters [filt ].length )) {
431- ret_value = true;
432- }
435+ ret_value = name_segment_matches_filter_value (name + name_len - filter -> length , filter -> value , filter -> length );
433436 break ;
434437
435438 case FILTER_STRING_PREFIX :
436- if (0 == strncmp (lower_name , filter_struct -> filters [filt ].value , filter_struct -> filters [filt ].length )) {
437- ret_value = true;
438- }
439+ ret_value = name_segment_matches_filter_value (name , filter -> value , filter -> length );
439440 break ;
440441
441442 case FILTER_STRING_FULLNAME :
442- if (0 == strncmp (lower_name , filter_struct -> filters [filt ].value , name_len )) {
443- ret_value = true;
444- }
443+ ret_value = (name_len == filter -> length ) && name_segment_matches_filter_value (name , filter -> value , filter -> length );
445444 break ;
446445 }
447446 if (ret_value ) {
0 commit comments