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