| /* { dg-do compile } */ |
| /* { dg-options "-O2 -std=gnu11 -fgnu89-inline" } */ |
| /* { dg-final { scan-assembler-not ".quad\[\\t \]+tunable_list" { target lp64 } } } */ |
| /* { dg-final { scan-assembler-not ".long\[\\t \]+tunable_list" { target { ! lp64 } } } } */ |
| |
| typedef unsigned long int size_t; |
| typedef long long int intmax_t; |
| typedef unsigned long long int uintmax_t; |
| typedef unsigned long long int uint64_t; |
| typedef intmax_t tunable_num_t; |
| typedef union |
| { |
| tunable_num_t numval; |
| const char *strval; |
| } tunable_val_t; |
| enum |
| { |
| HWCAP_X86_SSE2 = 1 << 0, |
| HWCAP_X86_64 = 1 << 1, |
| HWCAP_X86_AVX512_1 = 1 << 2 |
| }; |
| typedef void (*tunable_callback_t) (tunable_val_t *); |
| extern void *__minimal_malloc (size_t n) |
| __attribute__ ((visibility ("hidden"))); |
| extern int __libc_enable_secure __attribute__ ((section (".data.rel.ro"))); |
| extern uint64_t _dl_strtoul (const char *, char **) |
| __attribute__ ((visibility ("hidden"))); |
| extern void _dl_fatal_printf (const char *fmt, ...) |
| __attribute__ ((__format__ (__printf__, 1, 2), __noreturn__)); |
| typedef enum |
| { |
| glibc_rtld_nns, |
| glibc_elision_skip_lock_after_retries, |
| glibc_malloc_trim_threshold, |
| glibc_malloc_perturb, |
| glibc_cpu_x86_shared_cache_size, |
| glibc_pthread_rseq, |
| glibc_mem_tagging, |
| glibc_elision_tries, |
| glibc_elision_enable, |
| glibc_malloc_hugetlb, |
| glibc_cpu_x86_rep_movsb_threshold, |
| glibc_malloc_mxfast, |
| glibc_rtld_dynamic_sort, |
| glibc_elision_skip_lock_busy, |
| glibc_malloc_top_pad, |
| glibc_cpu_x86_rep_stosb_threshold, |
| glibc_cpu_x86_non_temporal_threshold, |
| glibc_cpu_x86_shstk, |
| glibc_pthread_stack_cache_size, |
| glibc_cpu_hwcap_mask, |
| glibc_malloc_mmap_max, |
| glibc_elision_skip_trylock_internal_abort, |
| glibc_malloc_tcache_unsorted_limit, |
| glibc_cpu_x86_ibt, |
| glibc_cpu_hwcaps, |
| glibc_elision_skip_lock_internal_abort, |
| glibc_malloc_arena_max, |
| glibc_malloc_mmap_threshold, |
| glibc_cpu_x86_data_cache_size, |
| glibc_malloc_tcache_count, |
| glibc_malloc_arena_test, |
| glibc_pthread_mutex_spin_count, |
| glibc_rtld_optional_static_tls, |
| glibc_malloc_tcache_max, |
| glibc_malloc_check, |
| } tunable_id_t; |
| typedef enum |
| { |
| TUNABLE_TYPE_INT_32, |
| TUNABLE_TYPE_UINT_64, |
| TUNABLE_TYPE_SIZE_T, |
| TUNABLE_TYPE_STRING |
| } tunable_type_code_t; |
| typedef struct |
| { |
| tunable_type_code_t type_code; |
| tunable_num_t min; |
| tunable_num_t max; |
| } tunable_type_t; |
| typedef enum |
| { |
| TUNABLE_SECLEVEL_SXID_ERASE = 0, |
| TUNABLE_SECLEVEL_SXID_IGNORE = 1, |
| TUNABLE_SECLEVEL_NONE = 2, |
| } tunable_seclevel_t; |
| struct _tunable |
| { |
| const char name[42]; |
| tunable_type_t type; |
| tunable_val_t val; |
| _Bool initialized; |
| tunable_seclevel_t security_level; |
| const char env_alias[23]; |
| }; |
| typedef struct _tunable tunable_t; |
| extern _Bool unsigned_tunable_type (tunable_type_code_t t); |
| |
| static tunable_t tunable_list[] __attribute__ ((section (".data.rel.ro"))) = { |
| { "glibc" |
| "." |
| "rtld" |
| "." |
| "nns", |
| { TUNABLE_TYPE_SIZE_T, 1, 16 }, |
| { .numval = 4 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "elision" |
| "." |
| "skip_lock_after_retries", |
| { TUNABLE_TYPE_INT_32, 0, (2147483647) }, |
| { .numval = 3 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "trim_threshold", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| "MALLOC_TRIM_THRESHOLD_" }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "perturb", |
| { TUNABLE_TYPE_INT_32, 0, 0xff }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| "MALLOC_PERTURB_" }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "x86_shared_cache_size", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "pthread" |
| "." |
| "rseq", |
| { TUNABLE_TYPE_INT_32, 0, 1 }, |
| { .numval = 1 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "mem" |
| "." |
| "tagging", |
| { TUNABLE_TYPE_INT_32, 0, 255 }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "elision" |
| "." |
| "tries", |
| { TUNABLE_TYPE_INT_32, 0, (2147483647) }, |
| { .numval = 3 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "elision" |
| "." |
| "enable", |
| { TUNABLE_TYPE_INT_32, 0, 1 }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "hugetlb", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "x86_rep_movsb_threshold", |
| { TUNABLE_TYPE_SIZE_T, 1, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "mxfast", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "rtld" |
| "." |
| "dynamic_sort", |
| { TUNABLE_TYPE_INT_32, 1, 2 }, |
| { .numval = 2 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "elision" |
| "." |
| "skip_lock_busy", |
| { TUNABLE_TYPE_INT_32, 0, (2147483647) }, |
| { .numval = 3 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "top_pad", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| "MALLOC_TOP_PAD_" }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "x86_rep_stosb_threshold", |
| { TUNABLE_TYPE_SIZE_T, 1, (18446744073709551615UL) }, |
| { .numval = 2048 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "x86_non_temporal_threshold", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "x86_shstk", |
| { TUNABLE_TYPE_STRING, 0, 0 }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "pthread" |
| "." |
| "stack_cache_size", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| { .numval = 41943040 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "hwcap_mask", |
| { TUNABLE_TYPE_UINT_64, 0, (18446744073709551615UL) }, |
| { .numval = (HWCAP_X86_64 | HWCAP_X86_AVX512_1) }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| "LD_HWCAP_MASK" }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "mmap_max", |
| { TUNABLE_TYPE_INT_32, 0, (2147483647) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| "MALLOC_MMAP_MAX_" }, |
| { "glibc" |
| "." |
| "elision" |
| "." |
| "skip_trylock_internal_abort", |
| { TUNABLE_TYPE_INT_32, 0, (2147483647) }, |
| { .numval = 3 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "tcache_unsorted_limit", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "x86_ibt", |
| { TUNABLE_TYPE_STRING, 0, 0 }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "hwcaps", |
| { TUNABLE_TYPE_STRING, 0, 0 }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "elision" |
| "." |
| "skip_lock_internal_abort", |
| { TUNABLE_TYPE_INT_32, 0, (2147483647) }, |
| { .numval = 3 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "arena_max", |
| { TUNABLE_TYPE_SIZE_T, 1, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| "MALLOC_ARENA_MAX" }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "mmap_threshold", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| "MALLOC_MMAP_THRESHOLD_" }, |
| { "glibc" |
| "." |
| "cpu" |
| "." |
| "x86_data_cache_size", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "tcache_count", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "arena_test", |
| { TUNABLE_TYPE_SIZE_T, 1, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_IGNORE, |
| "MALLOC_ARENA_TEST" }, |
| { "glibc" |
| "." |
| "pthread" |
| "." |
| "mutex_spin_count", |
| { TUNABLE_TYPE_INT_32, 0, 32767 }, |
| { .numval = 100 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "rtld" |
| "." |
| "optional_static_tls", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| { .numval = 512 }, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "tcache_max", |
| { TUNABLE_TYPE_SIZE_T, 0, (18446744073709551615UL) }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| { 0 } }, |
| { "glibc" |
| "." |
| "malloc" |
| "." |
| "check", |
| { TUNABLE_TYPE_INT_32, 0, 3 }, |
| {}, |
| ((void *)0), |
| TUNABLE_SECLEVEL_SXID_ERASE, |
| "MALLOC_CHECK_" }, |
| }; |
| extern void __tunables_init (char **); |
| extern void __tunables_print (void); |
| extern void __tunable_get_val (tunable_id_t, void *, tunable_callback_t); |
| extern void __tunable_set_val (tunable_id_t, tunable_val_t *, tunable_num_t *, |
| tunable_num_t *); |
| static __inline __attribute__ ((__always_inline__)) _Bool |
| tunable_val_lt (tunable_num_t lhs, tunable_num_t rhs, _Bool unsigned_cmp) |
| { |
| if (unsigned_cmp) |
| return (uintmax_t)lhs < (uintmax_t)rhs; |
| else |
| return lhs < rhs; |
| } |
| static __inline __attribute__ ((__always_inline__)) _Bool |
| tunable_val_gt (tunable_num_t lhs, tunable_num_t rhs, _Bool unsigned_cmp) |
| { |
| if (unsigned_cmp) |
| return (uintmax_t)lhs > (uintmax_t)rhs; |
| else |
| return lhs > rhs; |
| } |
| static __inline __attribute__ ((__always_inline__)) _Bool |
| tunable_is_name (const char *orig, const char *envname) |
| { |
| for (; *orig != '\0' && *envname != '\0'; envname++, orig++) |
| if (*orig != *envname) |
| break; |
| if (*orig == '\0' && *envname == '=') |
| return 1; |
| else |
| return 0; |
| } |
| static char * |
| tunables_strdup (const char *in) |
| { |
| size_t i = 0; |
| while (in[i++] != '\0') |
| ; |
| char *out = __minimal_malloc (i + 1); |
| if (out == ((void *)0)) |
| _dl_fatal_printf ("failed to allocate memory to process tunables\n"); |
| while (i-- > 0) |
| out[i] = in[i]; |
| return out; |
| } |
| static char ** |
| get_next_env (char **envp, char **name, size_t *namelen, char **val, |
| char ***prev_envp) |
| { |
| while (envp != ((void *)0) && *envp != ((void *)0)) |
| { |
| char **prev = envp; |
| char *envline = *envp++; |
| int len = 0; |
| while (envline[len] != '\0' && envline[len] != '=') |
| len++; |
| if (envline[len] == '\0') |
| continue; |
| *name = envline; |
| *namelen = len; |
| *val = &envline[len + 1]; |
| *prev_envp = prev; |
| return envp; |
| } |
| return ((void *)0); |
| } |
| static void |
| do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp, |
| const tunable_num_t *minp, const tunable_num_t *maxp) |
| { |
| tunable_num_t val, min, max; |
| if (cur->type.type_code == TUNABLE_TYPE_STRING) |
| { |
| cur->val.strval = valp->strval; |
| cur->initialized = 1; |
| return; |
| } |
| _Bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code); |
| val = valp->numval; |
| min = minp != ((void *)0) ? *minp : cur->type.min; |
| max = maxp != ((void *)0) ? *maxp : cur->type.max; |
| if (tunable_val_lt (min, cur->type.min, unsigned_cmp)) |
| min = cur->type.min; |
| if (tunable_val_gt (max, cur->type.max, unsigned_cmp)) |
| max = cur->type.max; |
| if (tunable_val_gt (min, max, unsigned_cmp)) |
| { |
| min = cur->type.min; |
| max = cur->type.max; |
| } |
| if (tunable_val_lt (val, min, unsigned_cmp) |
| || tunable_val_lt (max, val, unsigned_cmp)) |
| return; |
| cur->val.numval = val; |
| cur->type.min = min; |
| cur->type.max = max; |
| cur->initialized = 1; |
| } |
| static void |
| tunable_initialize (tunable_t *cur, const char *strval) |
| { |
| tunable_val_t val; |
| if (cur->type.type_code != TUNABLE_TYPE_STRING) |
| val.numval = (tunable_num_t)_dl_strtoul (strval, ((void *)0)); |
| else |
| val.strval = strval; |
| do_tunable_update_val (cur, &val, ((void *)0), ((void *)0)); |
| } |
| static void |
| parse_tunables (char *tunestr, char *valstring) |
| { |
| if (tunestr == ((void *)0) || *tunestr == '\0') |
| return; |
| char *p = tunestr; |
| size_t off = 0; |
| while (1) |
| { |
| char *name = p; |
| size_t len = 0; |
| while (p[len] != '=' && p[len] != ':' && p[len] != '\0') |
| len++; |
| if (p[len] == '\0') |
| { |
| if (__libc_enable_secure) |
| tunestr[off] = '\0'; |
| return; |
| } |
| if (p[len] == ':') |
| { |
| p += len + 1; |
| continue; |
| } |
| p += len + 1; |
| char *value = &valstring[p - tunestr]; |
| len = 0; |
| while (p[len] != ':' && p[len] != '\0') |
| len++; |
| for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++) |
| { |
| tunable_t *cur = &tunable_list[i]; |
| if (tunable_is_name (cur->name, name)) |
| { |
| if (__libc_enable_secure) |
| { |
| if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE) |
| { |
| if (off > 0) |
| tunestr[off++] = ':'; |
| const char *n = cur->name; |
| while (*n != '\0') |
| tunestr[off++] = *n++; |
| tunestr[off++] = '='; |
| for (size_t j = 0; j < len; j++) |
| tunestr[off++] = value[j]; |
| } |
| if (cur->security_level != TUNABLE_SECLEVEL_NONE) |
| break; |
| } |
| value[len] = '\0'; |
| tunable_initialize (cur, value); |
| break; |
| } |
| } |
| if (p[len] != '\0') |
| p += len + 1; |
| } |
| } |
| void |
| __tunables_init (char **envp) |
| { |
| char *envname = ((void *)0); |
| char *envval = ((void *)0); |
| size_t len = 0; |
| char **prev_envp = envp; |
| while ((envp = get_next_env (envp, &envname, &len, &envval, &prev_envp)) |
| != ((void *)0)) |
| { |
| if (tunable_is_name ("GLIBC_TUNABLES", envname)) |
| { |
| char *new_env = tunables_strdup (envname); |
| if (new_env != ((void *)0)) |
| parse_tunables (new_env + len + 1, envval); |
| *prev_envp = new_env; |
| continue; |
| } |
| for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++) |
| { |
| tunable_t *cur = &tunable_list[i]; |
| const char *name = cur->env_alias; |
| if (tunable_is_name (name, envname)) |
| { |
| tunable_initialize (cur, envval); |
| break; |
| } |
| } |
| } |
| } |