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