• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright (c) 2014-2023 The Khronos Group Inc.
4  * Copyright (c) 2014-2023 Valve Corporation
5  * Copyright (c) 2014-2023 LunarG, Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * Author: Jon Ashburn <jon@lunarg.com>
20  * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
21  * Author: Chia-I Wu <olvaffe@gmail.com>
22  * Author: Chia-I Wu <olv@lunarg.com>
23  * Author: Mark Lobodzinski <mark@LunarG.com>
24  * Author: Lenny Komow <lenny@lunarg.com>
25  * Author: Charles Giessen <charles@lunarg.com>
26  *
27  */
28 
29 #include "loader_environment.h"
30 
31 #include "allocation.h"
32 #include "loader.h"
33 #include "log.h"
34 #include "stack_allocation.h"
35 
36 #include <ctype.h>
37 #include "param/sys_param.h"
38 
39 // Environment variables
40 #if COMMON_UNIX_PLATFORMS
41 
is_high_integrity(void)42 bool is_high_integrity(void) { return geteuid() != getuid() || getegid() != getgid(); }
43 
loader_getenv(const char * name,const struct loader_instance * inst)44 char *loader_getenv(const char *name, const struct loader_instance *inst) {
45     if (NULL == name) return NULL;
46 #if defined(__OHOS__)
47     CachedHandle g_Handle = CachedParameterCreate(name, "");
48     int changed = 0;
49     const char *res = CachedParameterGetChanged(g_Handle, &changed);
50     loader_log(inst, VULKAN_LOADER_DEBUG_BIT | VULKAN_LOADER_INFO_BIT, 0, "loader_getenv name:%s, res:%s", name, res);
51     if (res == NULL || res[0] == '\0') {
52         return NULL;
53     }
54     return (char *)res;
55 #else
56     // No allocation of memory necessary for Linux, but we should at least touch
57     // the inst pointer to get rid of compiler warnings.
58     (void)inst;
59     return getenv(name);
60 #endif
61 }
62 
loader_secure_getenv(const char * name,const struct loader_instance * inst)63 char *loader_secure_getenv(const char *name, const struct loader_instance *inst) {
64 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
65     // Apple does not appear to have a secure getenv implementation.
66     // The main difference between secure getenv and getenv is that secure getenv
67     // returns NULL if the process is being run with elevated privileges by a normal user.
68     // The idea is to prevent the reading of malicious environment variables by a process
69     // that can do damage.
70     // This algorithm is derived from glibc code that sets an internal
71     // variable (__libc_enable_secure) if the process is running under setuid or setgid.
72     return is_high_integrity() ? NULL : loader_getenv(name, inst);
73 #elif defined(__Fuchsia__)
74     return loader_getenv(name, inst);
75 #else
76     // Linux
77     char *out;
78 #if defined(HAVE_SECURE_GETENV) && !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
79     (void)inst;
80     out = secure_getenv(name);
81 #elif defined(HAVE___SECURE_GETENV) && !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
82     (void)inst;
83     out = __secure_getenv(name);
84 #else
85     out = loader_getenv(name, inst);
86 #if !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
87     loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Loader is using non-secure environment variable lookup for %s", name);
88 #endif
89 #endif
90     return out;
91 #endif
92 }
93 
loader_free_getenv(char * val,const struct loader_instance * inst)94 void loader_free_getenv(char *val, const struct loader_instance *inst) {
95     // No freeing of memory necessary for Linux, but we should at least touch
96     // the val and inst pointers to get rid of compiler warnings.
97     (void)val;
98     (void)inst;
99 }
100 
101 #elif defined(WIN32)
102 
is_high_integrity()103 bool is_high_integrity() {
104     HANDLE process_token;
105     if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &process_token)) {
106         // Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD.
107         uint8_t mandatory_label_buffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)];
108         DWORD buffer_size;
109         if (GetTokenInformation(process_token, TokenIntegrityLevel, mandatory_label_buffer, sizeof(mandatory_label_buffer),
110                                 &buffer_size) != 0) {
111             const TOKEN_MANDATORY_LABEL *mandatory_label = (const TOKEN_MANDATORY_LABEL *)mandatory_label_buffer;
112             const DWORD sub_authority_count = *GetSidSubAuthorityCount(mandatory_label->Label.Sid);
113             const DWORD integrity_level = *GetSidSubAuthority(mandatory_label->Label.Sid, sub_authority_count - 1);
114 
115             CloseHandle(process_token);
116             return integrity_level >= SECURITY_MANDATORY_HIGH_RID;
117         }
118 
119         CloseHandle(process_token);
120     }
121 
122     return false;
123 }
124 
loader_getenv(const char * name,const struct loader_instance * inst)125 char *loader_getenv(const char *name, const struct loader_instance *inst) {
126     int name_utf16_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
127     if (name_utf16_size <= 0) {
128         return NULL;
129     }
130     wchar_t *name_utf16 = (wchar_t *)loader_stack_alloc(name_utf16_size * sizeof(wchar_t));
131     if (MultiByteToWideChar(CP_UTF8, 0, name, -1, name_utf16, name_utf16_size) != name_utf16_size) {
132         return NULL;
133     }
134 
135     DWORD val_size = GetEnvironmentVariableW(name_utf16, NULL, 0);
136     // val_size DOES include the null terminator, so for any set variable
137     // will always be at least 1. If it's 0, the variable wasn't set.
138     if (val_size == 0) {
139         return NULL;
140     }
141 
142     wchar_t *val = (wchar_t *)loader_stack_alloc(val_size * sizeof(wchar_t));
143     if (GetEnvironmentVariableW(name_utf16, val, val_size) != val_size - 1) {
144         return NULL;
145     }
146 
147     int val_utf8_size = WideCharToMultiByte(CP_UTF8, 0, val, -1, NULL, 0, NULL, NULL);
148     if (val_utf8_size <= 0) {
149         return NULL;
150     }
151     char *val_utf8 = (char *)loader_instance_heap_alloc(inst, val_utf8_size * sizeof(char), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
152     if (val_utf8 == NULL) {
153         return NULL;
154     }
155     if (WideCharToMultiByte(CP_UTF8, 0, val, -1, val_utf8, val_utf8_size, NULL, NULL) != val_utf8_size) {
156         loader_instance_heap_free(inst, val_utf8);
157         return NULL;
158     }
159     return val_utf8;
160 }
161 
loader_secure_getenv(const char * name,const struct loader_instance * inst)162 char *loader_secure_getenv(const char *name, const struct loader_instance *inst) {
163     if (NULL == name) return NULL;
164 #if !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
165     if (is_high_integrity()) {
166         loader_log(inst, VULKAN_LOADER_INFO_BIT, 0,
167                    "Loader is running with elevated permissions. Environment variable %s will be ignored", name);
168         return NULL;
169     }
170 #endif
171 
172     return loader_getenv(name, inst);
173 }
174 
loader_free_getenv(char * val,const struct loader_instance * inst)175 void loader_free_getenv(char *val, const struct loader_instance *inst) { loader_instance_heap_free(inst, (void *)val); }
176 
177 #else
178 
179 #warning \
180     "This platform does not support environment variables! If this is not intended, please implement the stubs functions loader_getenv and loader_free_getenv"
181 
loader_getenv(const char * name,const struct loader_instance * inst)182 char *loader_getenv(const char *name, const struct loader_instance *inst) {
183     // stub func
184     (void)inst;
185     (void)name;
186     return NULL;
187 }
loader_free_getenv(char * val,const struct loader_instance * inst)188 void loader_free_getenv(char *val, const struct loader_instance *inst) {
189     // stub func
190     (void)val;
191     (void)inst;
192 }
193 
194 #endif
195 
196 // Determine the type of filter string based on the contents of it.
197 // This will properly check against:
198 //  - substrings "*string*"
199 //  - prefixes "string*"
200 //  - suffixes "*string"
201 //  - full string names "string"
202 // It will also return the correct start and finish to remove any star '*' characters for the actual string compare
determine_filter_type(const char * filter_string,enum loader_filter_string_type * filter_type,const char ** new_start,size_t * new_length)203 void determine_filter_type(const char *filter_string, enum loader_filter_string_type *filter_type, const char **new_start,
204                            size_t *new_length) {
205     size_t filter_length = strlen(filter_string);
206     bool star_begin = false;
207     bool star_end = false;
208     if ('~' == filter_string[0]) {
209         // One of the special identifiers like: ~all~, ~implicit~, or ~explicit~
210         *filter_type = FILTER_STRING_SPECIAL;
211         *new_start = filter_string;
212         *new_length = filter_length;
213     } else {
214         if ('*' == filter_string[0]) {
215             // Only the * means everything
216             if (filter_length == 1) {
217                 *filter_type = FILTER_STRING_SPECIAL;
218                 *new_start = filter_string;
219                 *new_length = filter_length;
220             } else {
221                 star_begin = true;
222             }
223         }
224         if ('*' == filter_string[filter_length - 1]) {
225             // Not really valid, but just catch this case so if someone accidentally types "**" it will also mean everything
226             if (filter_length == 2) {
227                 *filter_type = FILTER_STRING_SPECIAL;
228                 *new_start = filter_string;
229                 *new_length = filter_length;
230             } else {
231                 star_end = true;
232             }
233         }
234         if (star_begin && star_end) {
235             *filter_type = FILTER_STRING_SUBSTRING;
236             *new_start = &filter_string[1];
237             *new_length = filter_length - 2;
238         } else if (star_begin) {
239             *new_start = &filter_string[1];
240             *new_length = filter_length - 1;
241             *filter_type = FILTER_STRING_SUFFIX;
242         } else if (star_end) {
243             *filter_type = FILTER_STRING_PREFIX;
244             *new_start = filter_string;
245             *new_length = filter_length - 1;
246         } else {
247             *filter_type = FILTER_STRING_FULLNAME;
248             *new_start = filter_string;
249             *new_length = filter_length;
250         }
251     }
252 }
253 
254 // Parse the provided filter string provided by the envrionment variable into the appropriate filter
255 // struct variable.
parse_generic_filter_environment_var(const struct loader_instance * inst,const char * env_var_name,struct loader_envvar_filter * filter_struct)256 VkResult parse_generic_filter_environment_var(const struct loader_instance *inst, const char *env_var_name,
257                                               struct loader_envvar_filter *filter_struct) {
258     VkResult result = VK_SUCCESS;
259     memset(filter_struct, 0, sizeof(struct loader_envvar_filter));
260     char *parsing_string = NULL;
261     char *env_var_value = loader_secure_getenv(env_var_name, inst);
262     if (NULL == env_var_value) {
263         return result;
264     }
265     const size_t env_var_len = strlen(env_var_value);
266     if (env_var_len == 0) {
267         goto out;
268     }
269     // Allocate a separate string since scan_for_next_comma modifies the original string
270     parsing_string = loader_instance_heap_calloc(inst, env_var_len + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
271     if (NULL == parsing_string) {
272         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
273                    "parse_generic_filter_environment_var: Failed to allocate space for parsing env var \'%s\'", env_var_name);
274         result = VK_ERROR_OUT_OF_HOST_MEMORY;
275         goto out;
276     }
277 
278     for (uint32_t iii = 0; iii < env_var_len; ++iii) {
279         parsing_string[iii] = (char)tolower(env_var_value[iii]);
280     }
281     parsing_string[env_var_len] = '\0';
282 
283     char *context = NULL;
284     char *token = thread_safe_strtok(parsing_string, ",", &context);
285     while (NULL != token) {
286         enum loader_filter_string_type cur_filter_type;
287         const char *actual_start;
288         size_t actual_len;
289         determine_filter_type(token, &cur_filter_type, &actual_start, &actual_len);
290         if (actual_len > VK_MAX_EXTENSION_NAME_SIZE) {
291             loader_strncpy(filter_struct->filters[filter_struct->count].value, VK_MAX_EXTENSION_NAME_SIZE, actual_start,
292                            VK_MAX_EXTENSION_NAME_SIZE);
293         } else {
294             loader_strncpy(filter_struct->filters[filter_struct->count].value, VK_MAX_EXTENSION_NAME_SIZE, actual_start,
295                            actual_len);
296         }
297         filter_struct->filters[filter_struct->count].length = actual_len;
298         filter_struct->filters[filter_struct->count++].type = cur_filter_type;
299         if (filter_struct->count >= MAX_ADDITIONAL_FILTERS) {
300             break;
301         }
302         token = thread_safe_strtok(NULL, ",", &context);
303     }
304 
305 out:
306 
307     loader_instance_heap_free(inst, parsing_string);
308     loader_free_getenv(env_var_value, inst);
309     return result;
310 }
311 
312 // Parse the disable layer string.  The layer disable has some special behavior because we allow it to disable
313 // all layers (either with "~all~", "*", or "**"), all implicit layers (with "~implicit~"), and all explicit layers
314 // (with "~explicit~"), in addition to the other layer filtering behavior.
parse_layers_disable_filter_environment_var(const struct loader_instance * inst,struct loader_envvar_disable_layers_filter * disable_struct)315 VkResult parse_layers_disable_filter_environment_var(const struct loader_instance *inst,
316                                                      struct loader_envvar_disable_layers_filter *disable_struct) {
317     VkResult result = VK_SUCCESS;
318     memset(disable_struct, 0, sizeof(struct loader_envvar_disable_layers_filter));
319     char *parsing_string = NULL;
320     char *env_var_value = loader_secure_getenv(VK_LAYERS_DISABLE_ENV_VAR, inst);
321     if (NULL == env_var_value) {
322         goto out;
323     }
324     const size_t env_var_len = strlen(env_var_value);
325     if (env_var_len == 0) {
326         goto out;
327     }
328     // Allocate a separate string since scan_for_next_comma modifies the original string
329     parsing_string = loader_instance_heap_calloc(inst, env_var_len + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
330     if (NULL == parsing_string) {
331         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
332                    "parse_layers_disable_filter_environment_var: Failed to allocate space for parsing env var "
333                    "\'VK_LAYERS_DISABLE_ENV_VAR\'");
334         result = VK_ERROR_OUT_OF_HOST_MEMORY;
335         goto out;
336     }
337 
338     for (uint32_t iii = 0; iii < env_var_len; ++iii) {
339         parsing_string[iii] = (char)tolower(env_var_value[iii]);
340     }
341     parsing_string[env_var_len] = '\0';
342 
343     char *context = NULL;
344     char *token = thread_safe_strtok(parsing_string, ",", &context);
345     while (NULL != token) {
346         uint32_t cur_count = disable_struct->additional_filters.count;
347         enum loader_filter_string_type cur_filter_type;
348         const char *actual_start;
349         size_t actual_len;
350         determine_filter_type(token, &cur_filter_type, &actual_start, &actual_len);
351         if (cur_filter_type == FILTER_STRING_SPECIAL) {
352             if (!strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_1, token) || !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_2, token) ||
353                 !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_3, token)) {
354                 disable_struct->disable_all = true;
355             } else if (!strcmp(VK_LOADER_DISABLE_IMPLICIT_LAYERS_VAR, token)) {
356                 disable_struct->disable_all_implicit = true;
357             } else if (!strcmp(VK_LOADER_DISABLE_EXPLICIT_LAYERS_VAR, token)) {
358                 disable_struct->disable_all_explicit = true;
359             }
360         } else {
361             if (actual_len > VK_MAX_EXTENSION_NAME_SIZE) {
362                 loader_strncpy(disable_struct->additional_filters.filters[cur_count].value, VK_MAX_EXTENSION_NAME_SIZE,
363                                actual_start, VK_MAX_EXTENSION_NAME_SIZE);
364             } else {
365                 loader_strncpy(disable_struct->additional_filters.filters[cur_count].value, VK_MAX_EXTENSION_NAME_SIZE,
366                                actual_start, actual_len);
367             }
368             disable_struct->additional_filters.filters[cur_count].length = actual_len;
369             disable_struct->additional_filters.filters[cur_count].type = cur_filter_type;
370             disable_struct->additional_filters.count++;
371             if (disable_struct->additional_filters.count >= MAX_ADDITIONAL_FILTERS) {
372                 break;
373             }
374         }
375         token = thread_safe_strtok(NULL, ",", &context);
376     }
377 out:
378     loader_instance_heap_free(inst, parsing_string);
379     loader_free_getenv(env_var_value, inst);
380     return result;
381 }
382 
383 // Parses the filter environment variables to determine if we have any special behavior
parse_layer_environment_var_filters(const struct loader_instance * inst,struct loader_envvar_all_filters * layer_filters)384 VkResult parse_layer_environment_var_filters(const struct loader_instance *inst, struct loader_envvar_all_filters *layer_filters) {
385     VkResult res = parse_generic_filter_environment_var(inst, VK_LAYERS_ENABLE_ENV_VAR, &layer_filters->enable_filter);
386     if (VK_SUCCESS != res) {
387         return res;
388     }
389     res = parse_layers_disable_filter_environment_var(inst, &layer_filters->disable_filter);
390     if (VK_SUCCESS != res) {
391         return res;
392     }
393     res = parse_generic_filter_environment_var(inst, VK_LAYERS_ALLOW_ENV_VAR, &layer_filters->allow_filter);
394     if (VK_SUCCESS != res) {
395         return res;
396     }
397     return res;
398 }
399 
400 // Check to see if the provided layer name matches any of the filter strings.
401 // This will properly check against:
402 //  - substrings "*string*"
403 //  - prefixes "string*"
404 //  - suffixes "*string"
405 //  - full string names "string"
check_name_matches_filter_environment_var(const char * name,const struct loader_envvar_filter * filter_struct)406 bool check_name_matches_filter_environment_var(const char *name, const struct loader_envvar_filter *filter_struct) {
407     bool ret_value = false;
408     const size_t name_len = strlen(name);
409     char lower_name[VK_MAX_EXTENSION_NAME_SIZE];
410     for (uint32_t iii = 0; iii < name_len; ++iii) {
411         lower_name[iii] = (char)tolower(name[iii]);
412     }
413     lower_name[name_len] = '\0';
414     for (uint32_t filt = 0; filt < filter_struct->count; ++filt) {
415         // Check if the filter name is longer (this is with all special characters removed), and if it is
416         // continue since it can't match.
417         if (filter_struct->filters[filt].length > name_len) {
418             continue;
419         }
420         switch (filter_struct->filters[filt].type) {
421             case FILTER_STRING_SPECIAL:
422                 if (!strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_1, filter_struct->filters[filt].value) ||
423                     !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_2, filter_struct->filters[filt].value) ||
424                     !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_3, filter_struct->filters[filt].value)) {
425                     ret_value = true;
426                 }
427                 break;
428 
429             case FILTER_STRING_SUBSTRING:
430                 if (NULL != strstr(lower_name, filter_struct->filters[filt].value)) {
431                     ret_value = true;
432                 }
433                 break;
434 
435             case FILTER_STRING_SUFFIX:
436                 if (0 == strncmp(lower_name + name_len - filter_struct->filters[filt].length, filter_struct->filters[filt].value,
437                                  filter_struct->filters[filt].length)) {
438                     ret_value = true;
439                 }
440                 break;
441 
442             case FILTER_STRING_PREFIX:
443                 if (0 == strncmp(lower_name, filter_struct->filters[filt].value, filter_struct->filters[filt].length)) {
444                     ret_value = true;
445                 }
446                 break;
447 
448             case FILTER_STRING_FULLNAME:
449                 if (0 == strncmp(lower_name, filter_struct->filters[filt].value, name_len)) {
450                     ret_value = true;
451                 }
452                 break;
453         }
454         if (ret_value) {
455             break;
456         }
457     }
458     return ret_value;
459 }
460 
461 // Get the layer name(s) from the env_name environment variable. If layer is found in
462 // search_list then add it to layer_list.  But only add it to layer_list if type_flags matches.
loader_add_environment_layers(struct loader_instance * inst,const enum layer_type_flags type_flags,const struct loader_envvar_all_filters * filters,struct loader_pointer_layer_list * target_list,struct loader_pointer_layer_list * expanded_target_list,const struct loader_layer_list * source_list)463 VkResult loader_add_environment_layers(struct loader_instance *inst, const enum layer_type_flags type_flags,
464                                        const struct loader_envvar_all_filters *filters,
465                                        struct loader_pointer_layer_list *target_list,
466                                        struct loader_pointer_layer_list *expanded_target_list,
467                                        const struct loader_layer_list *source_list) {
468     VkResult res = VK_SUCCESS;
469     char *layer_env = loader_getenv(ENABLED_LAYERS_ENV, inst);
470 
471     // If the layer environment variable is present (i.e. VK_INSTANCE_LAYERS), we will always add it to the layer list.
472     if (layer_env != NULL) {
473         size_t layer_env_len = strlen(layer_env) + 1;
474         char *name = loader_stack_alloc(layer_env_len);
475         if (name != NULL) {
476             loader_strncpy(name, layer_env_len, layer_env, layer_env_len);
477 
478             loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "env var \'%s\' defined and adding layers \"%s\"",
479                        ENABLED_LAYERS_ENV, name);
480 
481             // First look for the old-fashion layers forced on with VK_INSTANCE_LAYERS
482             while (name && *name) {
483                 char *next = loader_get_next_path(name);
484 
485                 if (strlen(name) > 0) {
486                     bool found = false;
487                     for (uint32_t i = 0; i < source_list->count; i++) {
488                         struct loader_layer_properties *source_prop = &source_list->list[i];
489 
490                         if (0 == strcmp(name, source_prop->info.layerName)) {
491                             found = true;
492                             // Only add it if it doesn't already appear in the layer list
493                             if (!loader_find_layer_name_in_list(source_prop->info.layerName, target_list)) {
494                                 if (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) {
495                                     source_prop->enabled_by_what = ENABLED_BY_WHAT_VK_INSTANCE_LAYERS;
496                                     res = loader_add_layer_properties_to_list(inst, target_list, source_prop);
497                                     if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
498                                     res = loader_add_layer_properties_to_list(inst, expanded_target_list, source_prop);
499                                     if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
500                                 } else {
501                                     res = loader_add_meta_layer(inst, filters, source_prop, target_list, expanded_target_list,
502                                                                 source_list, NULL);
503                                     if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
504                                 }
505                                 break;
506                             }
507                         }
508                     }
509                     if (!found) {
510                         loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_LAYER_BIT, 0,
511                                    "Layer \"%s\" was not found but was requested by env var VK_INSTANCE_LAYERS!", name);
512                     }
513                 }
514                 name = next;
515             }
516         }
517     }
518 
519     // Loop through all the layers and check the enable/disable filters
520     for (uint32_t i = 0; i < source_list->count; i++) {
521         struct loader_layer_properties *source_prop = &source_list->list[i];
522 
523         // If it doesn't match the type, or the name isn't what we're looking for, just continue
524         if ((source_prop->type_flags & type_flags) != type_flags) {
525             continue;
526         }
527 
528         // We found a layer we're interested in, but has it been disabled...
529         bool adding = true;
530         bool is_implicit = (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER));
531         bool disabled_by_type =
532             (is_implicit) ? (filters->disable_filter.disable_all_implicit) : (filters->disable_filter.disable_all_explicit);
533         if ((filters->disable_filter.disable_all || disabled_by_type ||
534              check_name_matches_filter_environment_var(source_prop->info.layerName, &filters->disable_filter.additional_filters)) &&
535             !check_name_matches_filter_environment_var(source_prop->info.layerName, &filters->allow_filter)) {
536             loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0,
537                        "Layer \"%s\" ignored because it has been disabled by env var \'%s\'", source_prop->info.layerName,
538                        VK_LAYERS_DISABLE_ENV_VAR);
539             adding = false;
540         }
541 
542         // If we are supposed to filter through all layers, we need to compare the layer name against the filter.
543         // This can override the disable above, so we want to do it second.
544         // Also make sure the layer isn't already in the output_list, skip adding it if it is.
545         if (check_name_matches_filter_environment_var(source_prop->info.layerName, &filters->enable_filter) &&
546             !loader_find_layer_name_in_list(source_prop->info.layerName, target_list)) {
547             adding = true;
548             // Only way is_substring is true is if there are enable variables.  If that's the case, and we're past the
549             // above, we should indicate that it was forced on in this way.
550             loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0,
551                        "Layer \"%s\" forced enabled due to env var \'%s\'", source_prop->info.layerName, VK_LAYERS_ENABLE_ENV_VAR);
552         } else {
553             adding = false;
554         }
555 
556         if (!adding) {
557             continue;
558         }
559 
560         // If not a meta-layer, simply add it.
561         if (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) {
562             source_prop->enabled_by_what = ENABLED_BY_WHAT_VK_LOADER_LAYERS_ENABLE;
563             res = loader_add_layer_properties_to_list(inst, target_list, source_prop);
564             if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
565             res = loader_add_layer_properties_to_list(inst, expanded_target_list, source_prop);
566             if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
567         } else {
568             res = loader_add_meta_layer(inst, filters, source_prop, target_list, expanded_target_list, source_list, NULL);
569             if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
570         }
571     }
572 
573 out:
574 
575     if (layer_env != NULL) {
576         loader_free_getenv(layer_env, inst);
577     }
578 
579     return res;
580 }
581