Coverage Report

Created: 2026-08-30 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vulkan-loader/loader/loader_environment.c
Line
Count
Source
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
2.48k
char *loader_getenv(const char *name, const struct loader_instance *inst) {
44
2.48k
    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
2.48k
    (void)inst;
48
    // NOLINTNEXTLINE(concurrency-mt-unsafe) - there is no portable reentrant getenv; the loader never calls setenv concurrently
49
2.48k
    return getenv(name);
50
2.48k
}
51
52
182k
char *loader_secure_getenv(const char *name, const struct loader_instance *inst) {
53
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
54
    // Apple does not appear to have a secure getenv implementation.
55
    // The main difference between secure getenv and getenv is that secure getenv
56
    // returns NULL if the process is being run with elevated privileges by a normal user.
57
    // The idea is to prevent the reading of malicious environment variables by a process
58
    // that can do damage.
59
    // This algorithm is derived from glibc code that sets an internal
60
    // variable (__libc_enable_secure) if the process is running under setuid or setgid.
61
    return is_high_integrity() ? NULL : loader_getenv(name, inst);
62
#elif defined(__Fuchsia__)
63
    return loader_getenv(name, inst);
64
#else
65
    // Linux
66
182k
    char *out;
67
182k
#if defined(HAVE_SECURE_GETENV) && !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
68
182k
    (void)inst;
69
182k
    out = secure_getenv(name);
70
#elif defined(HAVE___SECURE_GETENV) && !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
71
    (void)inst;
72
    out = __secure_getenv(name);
73
#else
74
    out = loader_getenv(name, inst);
75
#if !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
76
    loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Loader is using non-secure environment variable lookup for %s", name);
77
#endif
78
#endif
79
182k
    return out;
80
182k
#endif
81
182k
}
82
83
149k
void loader_free_getenv(const char *val, const struct loader_instance *inst) {
84
    // No freeing of memory necessary for Linux, but we should at least touch
85
    // the val and inst pointers to get rid of compiler warnings.
86
149k
    (void)val;
87
149k
    (void)inst;
88
149k
}
89
90
#elif defined(WIN32)
91
92
bool is_high_integrity() {
93
    HANDLE process_token;
94
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &process_token)) {
95
        // Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD.
96
        uint8_t mandatory_label_buffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)];
97
        DWORD buffer_size;
98
        if (GetTokenInformation(process_token, TokenIntegrityLevel, mandatory_label_buffer, sizeof(mandatory_label_buffer),
99
                                &buffer_size) != 0) {
100
            const TOKEN_MANDATORY_LABEL *mandatory_label = (const TOKEN_MANDATORY_LABEL *)mandatory_label_buffer;
101
            const DWORD sub_authority_count = *GetSidSubAuthorityCount(mandatory_label->Label.Sid);
102
            const DWORD integrity_level = *GetSidSubAuthority(mandatory_label->Label.Sid, sub_authority_count - 1);
103
104
            CloseHandle(process_token);
105
            return integrity_level >= SECURITY_MANDATORY_HIGH_RID;
106
        }
107
108
        CloseHandle(process_token);
109
    }
110
111
    return false;
112
}
113
114
char *loader_getenv(const char *name, const struct loader_instance *inst) {
115
    int name_utf16_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
116
    if (name_utf16_size <= 0) {
117
        return NULL;
118
    }
119
    wchar_t *name_utf16 = (wchar_t *)loader_stack_alloc(name_utf16_size * sizeof(wchar_t));
120
    if (MultiByteToWideChar(CP_UTF8, 0, name, -1, name_utf16, name_utf16_size) != name_utf16_size) {
121
        return NULL;
122
    }
123
124
    DWORD val_size = GetEnvironmentVariableW(name_utf16, NULL, 0);
125
    // val_size DOES include the null terminator, so for any set variable
126
    // will always be at least 1. If it's 0, the variable wasn't set.
127
    if (val_size == 0) {
128
        return NULL;
129
    }
130
131
    wchar_t *val = (wchar_t *)loader_stack_alloc(val_size * sizeof(wchar_t));
132
    if (GetEnvironmentVariableW(name_utf16, val, val_size) != val_size - 1) {
133
        return NULL;
134
    }
135
136
    int val_utf8_size = WideCharToMultiByte(CP_UTF8, 0, val, -1, NULL, 0, NULL, NULL);
137
    if (val_utf8_size <= 0) {
138
        return NULL;
139
    }
140
    char *val_utf8 = (char *)loader_instance_heap_alloc(inst, val_utf8_size * sizeof(char), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
141
    if (val_utf8 == NULL) {
142
        return NULL;
143
    }
144
    if (WideCharToMultiByte(CP_UTF8, 0, val, -1, val_utf8, val_utf8_size, NULL, NULL) != val_utf8_size) {
145
        loader_instance_heap_free(inst, val_utf8);
146
        return NULL;
147
    }
148
    return val_utf8;
149
}
150
151
char *loader_secure_getenv(const char *name, const struct loader_instance *inst) {
152
    if (NULL == name) return NULL;
153
#if !defined(LOADER_USE_UNSAFE_FILE_SEARCH)
154
    if (is_high_integrity()) {
155
        loader_log(inst, VULKAN_LOADER_INFO_BIT, 0,
156
                   "Loader is running with elevated permissions. Environment variable %s will be ignored", name);
157
        return NULL;
158
    }
159
#endif
160
161
    return loader_getenv(name, inst);
162
}
163
164
void loader_free_getenv(const char *val, const struct loader_instance *inst) { loader_instance_heap_free(inst, (void *)val); }
165
166
#else
167
168
#warning \
169
    "This platform does not support environment variables! If this is not intended, please implement the stubs functions loader_getenv and loader_free_getenv"
170
171
char *loader_getenv(const char *name, const struct loader_instance *inst) {
172
    // stub func
173
    (void)inst;
174
    (void)name;
175
    return NULL;
176
}
177
void loader_free_getenv(const char *val, const struct loader_instance *inst) {
178
    // stub func
179
    (void)val;
180
    (void)inst;
181
}
182
183
#endif
184
185
// Determine the type of filter string based on the contents of it.
186
// This will properly check against:
187
//  - substrings "*string*"
188
//  - prefixes "string*"
189
//  - suffixes "*string"
190
//  - full string names "string"
191
// It will also return the correct start and finish to remove any star '*' characters for the actual string compare
192
void determine_filter_type(const char *filter_string, enum loader_filter_string_type *filter_type, const char **new_start,
193
8.21k
                           size_t *new_length) {
194
8.21k
    size_t filter_length = strlen(filter_string);
195
8.21k
    bool star_begin = false;
196
8.21k
    bool star_end = false;
197
8.21k
    if ('~' == filter_string[0]) {
198
        // One of the special identifiers like: ~all~, ~implicit~, or ~explicit~
199
0
        *filter_type = FILTER_STRING_SPECIAL;
200
0
        *new_start = filter_string;
201
0
        *new_length = filter_length;
202
8.21k
    } else {
203
8.21k
        if ('*' == filter_string[0]) {
204
            // Only the * means everything
205
0
            if (filter_length == 1) {
206
0
                *filter_type = FILTER_STRING_SPECIAL;
207
0
                *new_start = filter_string;
208
0
                *new_length = filter_length;
209
0
                return;
210
0
            }
211
0
            star_begin = true;
212
0
        }
213
8.21k
        if ('*' == filter_string[filter_length - 1]) {
214
            // Not really valid, but just catch this case so if someone accidentally types "**" it will also mean everything
215
0
            if (star_begin && filter_length == 2) {
216
0
                *filter_type = FILTER_STRING_SPECIAL;
217
0
                *new_start = filter_string;
218
0
                *new_length = filter_length;
219
0
                return;
220
0
            }
221
0
            star_end = true;
222
0
        }
223
8.21k
        if (star_begin && star_end) {
224
0
            *filter_type = FILTER_STRING_SUBSTRING;
225
0
            *new_start = &filter_string[1];
226
0
            *new_length = filter_length - 2;
227
8.21k
        } else if (star_begin) {
228
0
            *new_start = &filter_string[1];
229
0
            *new_length = filter_length - 1;
230
0
            *filter_type = FILTER_STRING_SUFFIX;
231
8.21k
        } else if (star_end) {
232
0
            *filter_type = FILTER_STRING_PREFIX;
233
0
            *new_start = filter_string;
234
0
            *new_length = filter_length - 1;
235
8.21k
        } else {
236
8.21k
            *filter_type = FILTER_STRING_FULLNAME;
237
8.21k
            *new_start = filter_string;
238
8.21k
            *new_length = filter_length;
239
8.21k
        }
240
8.21k
    }
241
8.21k
}
242
243
// Parse the provided filter string provided by the envrionment variable into the appropriate filter
244
// struct variable.
245
VkResult parse_generic_filter_environment_var(const struct loader_instance *inst, const char *env_var_name,
246
18.0k
                                              struct loader_envvar_filter *filter_struct) {
247
18.0k
    VkResult result = VK_SUCCESS;
248
18.0k
    memset(filter_struct, 0, sizeof(struct loader_envvar_filter));
249
18.0k
    char *parsing_string = NULL;
250
18.0k
    char *env_var_value = loader_secure_getenv(env_var_name, inst);
251
18.0k
    if (NULL == env_var_value) {
252
9.80k
        return result;
253
9.80k
    }
254
8.21k
    const size_t env_var_len = strlen(env_var_value);
255
8.21k
    if (env_var_len == 0) {
256
0
        goto out;
257
0
    }
258
    // Allocate a separate string since scan_for_next_comma modifies the original string
259
8.21k
    parsing_string = loader_instance_heap_calloc(inst, env_var_len + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
260
8.21k
    if (NULL == parsing_string) {
261
0
        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
262
0
                   "parse_generic_filter_environment_var: Failed to allocate space for parsing env var \'%s\'", env_var_name);
263
0
        result = VK_ERROR_OUT_OF_HOST_MEMORY;
264
0
        goto out;
265
0
    }
266
267
32.8k
    for (uint32_t iii = 0; iii < env_var_len; ++iii) {
268
24.6k
        parsing_string[iii] = (char)tolower((unsigned char)env_var_value[iii]);
269
24.6k
    }
270
8.21k
    parsing_string[env_var_len] = '\0';
271
272
8.21k
    char *context = NULL;
273
8.21k
    char *token = thread_safe_strtok(parsing_string, ",", &context);
274
16.4k
    while (NULL != token) {
275
8.21k
        enum loader_filter_string_type cur_filter_type;
276
8.21k
        const char *actual_start;
277
8.21k
        size_t actual_len;
278
8.21k
        determine_filter_type(token, &cur_filter_type, &actual_start, &actual_len);
279
        // value holds at most VK_MAX_EXTENSION_NAME_SIZE - 1 chars plus the null terminator. Keep the stored length in sync
280
        // with what actually fits so the matcher never walks past the buffer, and make sure it stays null terminated.
281
8.21k
        size_t stored_len = actual_len < VK_MAX_EXTENSION_NAME_SIZE - 1 ? actual_len : VK_MAX_EXTENSION_NAME_SIZE - 1;
282
8.21k
        loader_strncpy(filter_struct->filters[filter_struct->count].value, VK_MAX_EXTENSION_NAME_SIZE, actual_start, stored_len);
283
8.21k
        filter_struct->filters[filter_struct->count].value[stored_len] = '\0';
284
8.21k
        filter_struct->filters[filter_struct->count].length = stored_len;
285
8.21k
        filter_struct->filters[filter_struct->count++].type = cur_filter_type;
286
8.21k
        if (filter_struct->count >= MAX_ADDITIONAL_FILTERS) {
287
0
            break;
288
0
        }
289
8.21k
        token = thread_safe_strtok(NULL, ",", &context);
290
8.21k
    }
291
292
8.21k
out:
293
294
8.21k
    loader_instance_heap_free(inst, parsing_string);
295
8.21k
    loader_free_getenv(env_var_value, inst);
296
8.21k
    return result;
297
8.21k
}
298
299
// Parse the disable layer string.  The layer disable has some special behavior because we allow it to disable
300
// all layers (either with "~all~", "*", or "**"), all implicit layers (with "~implicit~"), and all explicit layers
301
// (with "~explicit~"), in addition to the other layer filtering behavior.
302
VkResult parse_layers_disable_filter_environment_var(const struct loader_instance *inst,
303
8.21k
                                                     struct loader_envvar_disable_layers_filter *disable_struct) {
304
8.21k
    VkResult result = VK_SUCCESS;
305
8.21k
    memset(disable_struct, 0, sizeof(struct loader_envvar_disable_layers_filter));
306
8.21k
    char *parsing_string = NULL;
307
8.21k
    char *env_var_value = loader_secure_getenv(VK_LAYERS_DISABLE_ENV_VAR, inst);
308
8.21k
    if (NULL == env_var_value) {
309
8.21k
        goto out;
310
8.21k
    }
311
0
    const size_t env_var_len = strlen(env_var_value);
312
0
    if (env_var_len == 0) {
313
0
        goto out;
314
0
    }
315
    // Allocate a separate string since scan_for_next_comma modifies the original string
316
0
    parsing_string = loader_instance_heap_calloc(inst, env_var_len + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
317
0
    if (NULL == parsing_string) {
318
0
        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
319
0
                   "parse_layers_disable_filter_environment_var: Failed to allocate space for parsing env var "
320
0
                   "\'VK_LAYERS_DISABLE_ENV_VAR\'");
321
0
        result = VK_ERROR_OUT_OF_HOST_MEMORY;
322
0
        goto out;
323
0
    }
324
325
0
    for (uint32_t iii = 0; iii < env_var_len; ++iii) {
326
0
        parsing_string[iii] = (char)tolower((unsigned char)env_var_value[iii]);
327
0
    }
328
0
    parsing_string[env_var_len] = '\0';
329
330
0
    char *context = NULL;
331
0
    char *token = thread_safe_strtok(parsing_string, ",", &context);
332
0
    while (NULL != token) {
333
0
        uint32_t cur_count = disable_struct->additional_filters.count;
334
0
        enum loader_filter_string_type cur_filter_type;
335
0
        const char *actual_start;
336
0
        size_t actual_len;
337
0
        determine_filter_type(token, &cur_filter_type, &actual_start, &actual_len);
338
0
        if (cur_filter_type == FILTER_STRING_SPECIAL) {
339
0
            if (!strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_1, token) || !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_2, token) ||
340
0
                !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_3, token)) {
341
0
                disable_struct->disable_all = true;
342
0
            } else if (!strcmp(VK_LOADER_DISABLE_IMPLICIT_LAYERS_VAR, token)) {
343
0
                disable_struct->disable_all_implicit = true;
344
0
            } else if (!strcmp(VK_LOADER_DISABLE_EXPLICIT_LAYERS_VAR, token)) {
345
0
                disable_struct->disable_all_explicit = true;
346
0
            }
347
0
        } else {
348
            // Keep the stored length in sync with what actually fits (value is VK_MAX_EXTENSION_NAME_SIZE bytes including
349
            // the null terminator) so the matcher never reads past the buffer.
350
0
            size_t stored_len = actual_len < VK_MAX_EXTENSION_NAME_SIZE - 1 ? actual_len : VK_MAX_EXTENSION_NAME_SIZE - 1;
351
0
            loader_strncpy(disable_struct->additional_filters.filters[cur_count].value, VK_MAX_EXTENSION_NAME_SIZE, actual_start,
352
0
                           stored_len);
353
0
            disable_struct->additional_filters.filters[cur_count].value[stored_len] = '\0';
354
0
            disable_struct->additional_filters.filters[cur_count].length = stored_len;
355
0
            disable_struct->additional_filters.filters[cur_count].type = cur_filter_type;
356
0
            disable_struct->additional_filters.count++;
357
0
            if (disable_struct->additional_filters.count >= MAX_ADDITIONAL_FILTERS) {
358
0
                break;
359
0
            }
360
0
        }
361
0
        token = thread_safe_strtok(NULL, ",", &context);
362
0
    }
363
8.21k
out:
364
8.21k
    loader_instance_heap_free(inst, parsing_string);
365
8.21k
    loader_free_getenv(env_var_value, inst);
366
8.21k
    return result;
367
0
}
368
369
// Parses the filter environment variables to determine if we have any special behavior
370
8.21k
VkResult parse_layer_environment_var_filters(const struct loader_instance *inst, struct loader_envvar_all_filters *layer_filters) {
371
8.21k
    VkResult res = parse_generic_filter_environment_var(inst, VK_LAYERS_ENABLE_ENV_VAR, &layer_filters->enable_filter);
372
8.21k
    if (VK_SUCCESS != res) {
373
0
        return res;
374
0
    }
375
8.21k
    res = parse_layers_disable_filter_environment_var(inst, &layer_filters->disable_filter);
376
8.21k
    if (VK_SUCCESS != res) {
377
0
        return res;
378
0
    }
379
8.21k
    res = parse_generic_filter_environment_var(inst, VK_LAYERS_ALLOW_ENV_VAR, &layer_filters->allow_filter);
380
8.21k
    if (VK_SUCCESS != res) {
381
0
        return res;
382
0
    }
383
8.21k
    return res;
384
8.21k
}
385
386
// Case-insensitive compare of `count` bytes of a name against a filter value. Filter values are already lowercased when
387
// they get parsed (see parse_generic_filter_environment_var), so we only need to fold the name side as we go. The caller
388
// guarantees both sides have at least `count` valid bytes.
389
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - distinct, well documented roles (raw name vs. pre-lowercased filter)
390
2.49k
static bool name_segment_matches_filter_value(const char *name_segment, const char *lowercase_filter_value, size_t count) {
391
3.63k
    for (size_t iii = 0; iii < count; ++iii) {
392
3.28k
        if ((char)tolower((unsigned char)name_segment[iii]) != lowercase_filter_value[iii]) {
393
2.14k
            return false;
394
2.14k
        }
395
3.28k
    }
396
348
    return true;
397
2.49k
}
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"
405
330k
bool check_name_matches_filter_environment_var(const char *name, const struct loader_envvar_filter *filter_struct) {
406
330k
    bool ret_value = false;
407
330k
    size_t name_len = strlen(name);
408
    // Compare each filter against `name` directly. name_segment_matches_filter_value does a case-insensitive compare
409
    // (filter values are already lowercased at parse time), so there's no need to make a lowercased copy of name first
410
    // and no limit on how long name can be.
411
495k
    for (uint32_t filt = 0; filt < filter_struct->count; ++filt) {
412
165k
        const struct loader_envvar_filter_value *filter = &filter_struct->filters[filt];
413
        // The filter (with its wildcards stripped) is longer than the name, so it can't possibly match.
414
165k
        if (filter->length > name_len) {
415
128k
            continue;
416
128k
        }
417
37.0k
        switch (filter->type) {
418
0
            case FILTER_STRING_SPECIAL:
419
0
                if (!strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_1, filter->value) ||
420
0
                    !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_2, filter->value) ||
421
0
                    !strcmp(VK_LOADER_DISABLE_ALL_LAYERS_VAR_3, filter->value)) {
422
0
                    ret_value = true;
423
0
                }
424
0
                break;
425
426
0
            case FILTER_STRING_SUBSTRING:
427
                // Slide the filter along the name looking for a match, stopping as soon as one is found.
428
0
                for (size_t start = 0; start + filter->length <= name_len; ++start) {
429
0
                    if (name_segment_matches_filter_value(name + start, filter->value, filter->length)) {
430
0
                        ret_value = true;
431
0
                        break;
432
0
                    }
433
0
                }
434
0
                break;
435
436
0
            case FILTER_STRING_SUFFIX:
437
0
                ret_value = name_segment_matches_filter_value(name + name_len - filter->length, filter->value, filter->length);
438
0
                break;
439
440
0
            case FILTER_STRING_PREFIX:
441
0
                ret_value = name_segment_matches_filter_value(name, filter->value, filter->length);
442
0
                break;
443
444
37.0k
            case FILTER_STRING_FULLNAME:
445
37.0k
                ret_value = (name_len == filter->length) && name_segment_matches_filter_value(name, filter->value, filter->length);
446
37.0k
                break;
447
37.0k
        }
448
37.0k
        if (ret_value) {
449
348
            break;
450
348
        }
451
37.0k
    }
452
330k
    return ret_value;
453
330k
}
454
455
// Get the layer name(s) from the env_name environment variable. If layer is found in
456
// search_list then add it to layer_list.  But only add it to layer_list if type_flags matches.
457
VkResult loader_add_environment_layers(struct loader_instance *inst, const char *enabled_layers_env,
458
                                       const struct loader_envvar_all_filters *filters,
459
                                       struct loader_pointer_layer_list *target_list,
460
                                       struct loader_pointer_layer_list *expanded_target_list,
461
0
                                       const struct loader_layer_list *source_list) {
462
0
    VkResult res = VK_SUCCESS;
463
0
    const enum layer_type_flags type_flags = VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER;
464
465
    // If the layer environment variable is present (i.e. VK_INSTANCE_LAYERS), we will always add it to the layer list.
466
0
    if (enabled_layers_env != NULL) {
467
0
        size_t layer_env_len = strlen(enabled_layers_env) + 1;
468
0
        char *name = loader_stack_alloc(layer_env_len);
469
0
        if (name != NULL) {
470
0
            loader_strncpy(name, layer_env_len, enabled_layers_env, layer_env_len);
471
472
0
            loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "env var \'%s\' defined and adding layers \"%s\"",
473
0
                       ENABLED_LAYERS_ENV, name);
474
475
            // First look for the old-fashion layers forced on with VK_INSTANCE_LAYERS
476
0
            while (name && *name) {
477
0
                char *next = loader_get_next_path(name);
478
479
0
                if (strlen(name) > 0) {
480
0
                    bool found = false;
481
0
                    for (uint32_t i = 0; i < source_list->count; i++) {
482
0
                        struct loader_layer_properties *source_prop = &source_list->list[i];
483
484
0
                        if (0 == strcmp(name, source_prop->info.layerName)) {
485
0
                            found = true;
486
                            // Only add it if it doesn't already appear in the layer list
487
0
                            if (!loader_find_layer_name_in_list(source_prop->info.layerName, target_list)) {
488
0
                                if (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) {
489
0
                                    source_prop->enabled_by_what = ENABLED_BY_WHAT_VK_INSTANCE_LAYERS;
490
0
                                    res = loader_add_layer_properties_to_list(inst, target_list, source_prop);
491
0
                                    if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
492
0
                                    res = loader_add_layer_properties_to_list(inst, expanded_target_list, source_prop);
493
0
                                    if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
494
0
                                } else {
495
0
                                    res = loader_add_meta_layer(inst, filters, source_prop, target_list, expanded_target_list,
496
0
                                                                source_list, NULL);
497
0
                                    if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
498
0
                                }
499
0
                                break;
500
0
                            }
501
0
                        }
502
0
                    }
503
0
                    if (!found) {
504
0
                        loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_LAYER_BIT, 0,
505
0
                                   "Layer \"%s\" was not found but was requested by env var VK_INSTANCE_LAYERS!", name);
506
0
                    }
507
0
                }
508
0
                name = next;
509
0
            }
510
0
        }
511
0
    }
512
513
    // Loop through all the layers and check the enable/disable filters
514
0
    for (uint32_t i = 0; i < source_list->count; i++) {
515
0
        struct loader_layer_properties *source_prop = &source_list->list[i];
516
517
        // If it doesn't match the type, or the name isn't what we're looking for, just continue
518
0
        if ((source_prop->type_flags & type_flags) != type_flags) {
519
0
            continue;
520
0
        }
521
522
        // A layer matching the enable filter is force-enabled even if it also matches the disable filter - this mirrors
523
        // VK_INSTANCE_LAYERS, which VK_LOADER_LAYERS_ENABLE is a generalization of, and which overrides disables.
524
        // Also make sure the layer isn't already in the output_list, skip adding it if it is.
525
0
        bool force_enabled = check_name_matches_filter_environment_var(source_prop->info.layerName, &filters->enable_filter) &&
526
0
                             !loader_find_layer_name_in_list(source_prop->info.layerName, target_list);
527
528
0
        bool is_implicit = (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER));
529
0
        bool disabled_by_type =
530
0
            is_implicit ? filters->disable_filter.disable_all_implicit : filters->disable_filter.disable_all_explicit;
531
0
        if (!force_enabled &&
532
0
            (filters->disable_filter.disable_all || disabled_by_type ||
533
0
             check_name_matches_filter_environment_var(source_prop->info.layerName, &filters->disable_filter.additional_filters)) &&
534
0
            !check_name_matches_filter_environment_var(source_prop->info.layerName, &filters->allow_filter)) {
535
0
            loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0,
536
0
                       "Layer \"%s\" ignored because it has been disabled by env var \'%s\'", source_prop->info.layerName,
537
0
                       VK_LAYERS_DISABLE_ENV_VAR);
538
0
            continue;
539
0
        }
540
541
0
        if (!force_enabled) {
542
0
            continue;
543
0
        }
544
        // Only way is_substring is true is if there are enable variables.  If that's the case, and we're past the
545
        // above, we should indicate that it was forced on in this way.
546
0
        loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Layer \"%s\" forced enabled due to env var \'%s\'",
547
0
                   source_prop->info.layerName, VK_LAYERS_ENABLE_ENV_VAR);
548
549
        // If not a meta-layer, simply add it.
550
0
        if (0 == (source_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) {
551
0
            source_prop->enabled_by_what = ENABLED_BY_WHAT_VK_LOADER_LAYERS_ENABLE;
552
0
            res = loader_add_layer_properties_to_list(inst, target_list, source_prop);
553
0
            if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
554
0
            res = loader_add_layer_properties_to_list(inst, expanded_target_list, source_prop);
555
0
            if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
556
0
        } else {
557
0
            res = loader_add_meta_layer(inst, filters, source_prop, target_list, expanded_target_list, source_list, NULL);
558
0
            if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
559
0
        }
560
0
    }
561
562
0
out:
563
564
0
    return res;
565
0
}
566
567
void parse_id_filter_environment_var(const struct loader_instance *inst, const char *env_var_name,
568
0
                                     struct loader_envvar_id_filter *filter_struct) {
569
0
    memset(filter_struct, 0, sizeof(struct loader_envvar_id_filter));
570
0
    char *parsing_string = NULL;
571
0
    char *env_var_value = loader_secure_getenv(env_var_name, inst);
572
0
    if (NULL == env_var_value) {
573
0
        return;
574
0
    }
575
0
    const size_t env_var_len = strlen(env_var_value);
576
0
    if (env_var_len == 0) {
577
0
        goto out;
578
0
    }
579
    // Allocate a separate string since scan_for_next_comma modifies the original string
580
0
    parsing_string = loader_stack_alloc(env_var_len + 1);
581
0
    for (uint32_t iii = 0; iii < env_var_len; ++iii) {
582
0
        parsing_string[iii] = (char)tolower((unsigned char)env_var_value[iii]);
583
0
    }
584
0
    parsing_string[env_var_len] = '\0';
585
586
0
    filter_struct->count = 0;
587
0
    char *context = NULL;
588
0
    char *token = thread_safe_strtok(parsing_string, ",", &context);
589
0
    while (NULL != token) {
590
0
        if (filter_struct->count >= MAX_ADDITIONAL_FILTERS) {
591
0
            loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
592
0
                       "parse_id_filter_environment_var: Exceeded maximum number of filters (%d) for env var '%s'. "
593
0
                       "Remaining entries will be ignored.",
594
0
                       MAX_ADDITIONAL_FILTERS, env_var_name);
595
0
            break;
596
0
        }
597
598
0
        struct loader_envvar_id_filter_value *filter_value = &filter_struct->filters[filter_struct->count];
599
600
0
        char *pEnd;
601
0
        filter_value->begin = (uint32_t)strtoul(token, &pEnd, 0);
602
603
0
        if (*pEnd != '\0') {
604
0
            pEnd++;
605
0
            filter_value->end = (uint32_t)strtoul(pEnd, NULL, 0);
606
0
        } else {
607
0
            filter_value->end = filter_value->begin;
608
0
        }
609
610
0
        filter_struct->count++;
611
0
        token = thread_safe_strtok(NULL, ",", &context);
612
0
    }
613
614
0
out:
615
616
0
    loader_free_getenv(env_var_value, inst);
617
0
}
618
619
0
bool check_id_matches_filter_environment_var(const uint32_t id, const struct loader_envvar_id_filter *filter_struct) {
620
0
    for (uint32_t i = 0; i < filter_struct->count; i++) {
621
0
        if ((filter_struct->filters[i].begin <= id) && (id <= filter_struct->filters[i].end)) {
622
0
            return true;
623
0
        }
624
0
    }
625
0
    return false;
626
0
}