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