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