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 41bool is_high_integrity() { return geteuid() != getuid() || getegid() != getgid(); } 42 43char *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 62char *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 93void 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 102bool 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 124char *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 161char *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 174void 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 181char *loader_getenv(const char *name, const struct loader_instance *inst) { 182 // stub func 183 (void)inst; 184 (void)name; 185 return NULL; 186} 187void 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 202void 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. 255VkResult 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 304out: 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. 314VkResult 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 } 376out: 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 383VkResult 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" 405bool 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. 462VkResult 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 570out: 571 572 if (layer_env != NULL) { 573 loader_free_getenv(layer_env, inst); 574 } 575 576 return res; 577} 578