From patchwork Tue Dec 5 01:48:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 129923 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 33A834BA23D9 for ; Wed, 11 Feb 2026 05:14:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 33A834BA23D9 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=KnmmgE0S X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id DB6B14BA23C3 for ; Wed, 11 Feb 2026 05:13:34 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DB6B14BA23C3 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DB6B14BA23C3 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770786815; cv=none; b=ljmyRH4sJ64HflhRXpyHdGVtIX5mrLnspajtQImrCmtXieA7+cNXEbi9/bL88f37YHp5zIjdYuFKxA7I4uBi3yQhqxxfDroSJ0b35W1K9YmkJybgXJ/uCMEz0ffYZBjzkBP2Wy3IF6SqQFRzGHympbFuERlGuo0v+idjtPca7TE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770786815; c=relaxed/simple; bh=FPDB7K6kRHTM5WQZqiU6Q1K48Kkolnhs/JoFq8rtS/o=; h=DKIM-Signature:To:Message-ID:From:Date:Subject; b=OhwSO8kQdqCzoBlBXBzi5lUL08N/HhacUbCjwJ9GtVZ+3ie3FNO+JBlmwjASmO00gkd4IZfveBE2ieO5jXEBw0R7u2xO0eQsw6WdlruoZ9VRZ4JMSR8N1s2qX7j957itqsBmcgXekoIjlyEdYY/8Jo1Tcd1o528v9J8DeTE+Ol0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DB6B14BA23C3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770786814; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:content-type:content-type:in-reply-to:in-reply-to: references:references; bh=tl1Rcc4k8Glw1MVS7+7Kd6RmFfVCa0pLk0C/SI+dDjQ=; b=KnmmgE0SlHQPT6pnRgvjKkH5XojD4rXWD2yidPFykzpFzvrlMsAwc1kjGNjpzwxhYg4lh0 X9ZmDWBxZf5Cr7ahf8hGO4m8PDtuBCeNpnw/JHYZ+bunqTc9KeJ/iTbs7Rq3MRJTdcl0E7 tQVAwFB++j1WuW0FnjErVeSsef1vVc8= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-292-dJedgbVFPF6Li3sQ6kVWVg-1; Wed, 11 Feb 2026 00:13:30 -0500 X-MC-Unique: dJedgbVFPF6Li3sQ6kVWVg-1 X-Mimecast-MFC-AGG-ID: dJedgbVFPF6Li3sQ6kVWVg_1770786810 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8650D1956051; Wed, 11 Feb 2026 05:13:29 +0000 (UTC) Received: from greed.delorie.com (unknown [10.22.89.244]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C0B4530001A8; Wed, 11 Feb 2026 05:13:28 +0000 (UTC) Received: from greed.delorie.com.redhat.com (localhost [127.0.0.1]) by greed.delorie.com (8.16.1/8.16.1) with ESMTP id 61B5DRTp921626; Wed, 11 Feb 2026 00:13:27 -0500 To: libc-alpha@sourceware.org CC: Sam James , Yury Khrustalev Message-ID: In-Reply-To: References: From: DJ Delorie Date: Mon, 4 Dec 2023 20:48:18 -0500 Subject: [PATCH v5 1/4] Add system-wide tunables: ldconfig part X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 2 X-Mimecast-MFC-PROC-ID: lxMkumZbhgRpekLORB0cS645RhWp_i_Ye73Qs_ak9hE_1770786810 X-Mimecast-Originator: redhat.com Content-type: text/plain; charset=UTF-8 X-Spam-Status: No, score=-7.8 required=5.0 tests=BAYES_00, DATE_IN_PAST_96_XX, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_ASCII_DIVIDERS, KAM_SHORT, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Adds support for reading /etc/tunables.conf The file contains one line per tunable, like this: glibc.foo.bar=14 glibc.malloc.more=0 Additionally, each line can be prefixed with a single character that controls overridability by the GLIBC_TUNABLES env var: !glibc.foo=0 ^ May be made more secure +glibc.foo=0 ^ May be overridden -glibc.foo=0 ^ May not be overridden Internally, each tunable will later have a default "overridability" and logic for what "more secure" means. The tunable cache format allows for a filter to be assigned to each tunable, to be used at program start to decide if a tunable applies to that program. No such filters have yet been specified. The cache format also stores a pre-parsed value for the tunable, and the ID of the tunable, to improve load-time performance. --- elf/Makefile | 1 + elf/cache.c | 65 +++++- elf/ldconfig.c | 21 +- elf/tunconf.c | 403 +++++++++++++++++++++++++++++++++++++ elf/tunconf.h | 40 ++++ sysdeps/generic/dl-cache.h | 6 + sysdeps/generic/ldconfig.h | 2 + 7 files changed, 534 insertions(+), 4 deletions(-) create mode 100644 elf/tunconf.c create mode 100644 elf/tunconf.h diff --git a/elf/Makefile b/elf/Makefile index 396e97b5e7..42da891f5a 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -229,6 +229,7 @@ ldconfig-modules := \ readlib \ static-stubs \ stringtable \ + tunconf \ xmalloc \ xstrdup \ # ldconfig-modules diff --git a/elf/cache.c b/elf/cache.c index a6dc85dc0f..e798f7e2d8 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -36,6 +36,7 @@ #include #include #include +#include /* Used to store library names, paths, and other strings. */ static struct stringtable strings; @@ -275,7 +276,8 @@ check_new_cache (struct cache_file_new *cache) /* Print the extension information in *EXT. */ static void -print_extensions (struct cache_extension_all_loaded *ext) +print_extensions (struct cache_extension_all_loaded *ext, + const char *cache_data) { if (ext->sections[cache_extension_tag_generator].base != NULL) { @@ -284,6 +286,30 @@ print_extensions (struct cache_extension_all_loaded *ext) ext->sections[cache_extension_tag_generator].size, stdout); putchar ('\n'); } + if (ext->sections[cache_extension_tag_tunables].base != NULL) + { + struct tunable_header_cached *thc; + struct tunable_entry_cached *tec; + int i, count; + + thc = (struct tunable_header_cached *) + ext->sections[cache_extension_tag_tunables].base; + tec = thc->tunables; + count = thc->num_tunables; + printf("tunables sig 0x%08x ver 0x%08x count %u\n", + thc->signature, thc->version, thc->num_tunables); + for (i = 0; i < count; ++ i) + { + printf(" [%d] %s : %s [flags 0x%08x", + i, + cache_data + tec[i].name_offset, + cache_data + tec[i].value_offset, + tec[i].flags); + if (tec[i].flag_offset != 0) + printf(" : %s", cache_data + tec[i].flag_offset); + printf("]\n"); + } + } } /* Print the whole cache file, if a file contains the new cache format @@ -394,7 +420,7 @@ print_cache (const char *cache_name) cache_new->libs[i].hwcap, hwcaps_string, cache_data + cache_new->libs[i].value); } - print_extensions (&ext); + print_extensions (&ext, cache_data); } /* Cleanup. */ munmap (cache, cache_size); @@ -498,6 +524,28 @@ write_extensions (int fd, uint32_t str_offset, ext->sections[xid].size = hwcaps_size; } + struct tunable_header_cached *tunable_data; + size_t tunable_size; + size_t tunable_aligner = 0; + + tunable_data = get_tunconf_ext (str_offset); + if (tunable_data != NULL) + { + uint32_t tunable_offset_ua; + uint32_t tunable_offset; + + tunable_size = TUNCONF_SIZE (tunable_data); + tunable_offset_ua = generator_offset + strlen (generator); + tunable_offset = ALIGN_UP (tunable_offset_ua, 8); + tunable_aligner = tunable_offset - tunable_offset_ua; + + ++xid; + ext->sections[xid].tag = cache_extension_tag_tunables; + ext->sections[xid].flags = 0; + ext->sections[xid].offset = tunable_offset; + ext->sections[xid].size = tunable_size; + } + ++xid; ext->count = xid; assert (xid <= cache_extension_count); @@ -509,6 +557,13 @@ write_extensions (int fd, uint32_t str_offset, || write (fd, generator, strlen (generator)) != strlen (generator)) error (EXIT_FAILURE, errno, _("Writing of cache extension data failed")); + if (tunable_data) + { + if (write (fd, " ", tunable_aligner) != tunable_aligner + || write (fd, tunable_data, tunable_size) != tunable_size) + error (EXIT_FAILURE, errno, _("Writing of cache tunable data failed")); + } + free (hwcaps_array); free (ext); } @@ -1106,3 +1161,9 @@ out_fail: free (temp_name); free (file_entries); } + +struct stringtable_entry * +cache_store_string (const char *string) +{ + return stringtable_add (&strings, string); +} diff --git a/elf/ldconfig.c b/elf/ldconfig.c index aca967403a..1bd81f4220 100644 --- a/elf/ldconfig.c +++ b/elf/ldconfig.c @@ -44,12 +44,17 @@ #include #include #include +#include "tunconf.h" #ifndef LD_SO_CONF # define LD_SO_CONF SYSCONFDIR "/ld.so.conf" #endif +#ifndef TUNABLES_CONF +# define TUNABLES_CONF SYSCONFDIR "/tunables.conf" +#endif + /* Get libc version number. */ #include @@ -107,9 +112,12 @@ static int opt_ignore_aux_cache; /* Cache file to use. */ static char *cache_file; -/* Configuration file. */ +/* Configuration file for libraries. */ static const char *config_file; +/* Configuration file for tunables. */ +static const char *tunconfig_file; + /* Name and version of program. */ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) @@ -127,7 +135,8 @@ static const struct argp_option options[] = { NULL, 'X', NULL, 0, N_("Don't update symbolic links"), 0}, { NULL, 'r', N_("ROOT"), 0, N_("Change to and use ROOT as root directory"), 0}, { NULL, 'C', N_("CACHE"), 0, N_("Use CACHE as cache file"), 0}, - { NULL, 'f', N_("CONF"), 0, N_("Use CONF as configuration file"), 0}, + { NULL, 'f', N_("CONF"), 0, N_("Use CONF as configuration file for libraries"), 0}, + { NULL, 't', N_("TUNCONF"), 0, N_("Use TUNCONF as configuration file for tunables"), 0}, { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line. Don't build cache."), 0}, { NULL, 'l', NULL, 0, N_("Manually link individual libraries."), 0}, { "format", 'c', N_("FORMAT"), 0, N_("Format to use: new (default), old, or compat"), 0}, @@ -164,6 +173,9 @@ parse_opt (int key, char *arg, struct argp_state *state) case 'f': config_file = arg; break; + case 't': + tunconfig_file = arg; + break; case 'i': opt_ignore_aux_cache = 1; break; @@ -1229,6 +1241,9 @@ main (int argc, char **argv) if (config_file == NULL) config_file = LD_SO_CONF; + if (tunconfig_file == NULL) + tunconfig_file = TUNABLES_CONF; + if (opt_print_cache) { if (opt_chroot != NULL) @@ -1304,6 +1319,8 @@ main (int argc, char **argv) search_dirs (); + parse_tunconf (tunconfig_file, true, opt_chroot); + if (opt_build_cache) { save_cache (cache_file); diff --git a/elf/tunconf.c b/elf/tunconf.c new file mode 100644 index 0000000000..ea547e95d5 --- /dev/null +++ b/elf/tunconf.c @@ -0,0 +1,403 @@ +/* Manage /etc/tunables.* + Copyright (C) 1999-2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TUNABLES_INTERNAL +#include +#include + +#include +#include +#include +#include + +#include "tunconf.h" + +/* Declared in chroot_canon.c. */ +extern char *chroot_canon (const char *chroot, const char *name); + +/*----------------------------------------------------------------------*/ + +#ifndef TUNABLES_CONF +# define TUNABLES_CONF SYSCONFDIR "/tunables.conf" +#endif + +#ifndef TUNABLES_CACHE +# define TUNABLES_CACHE SYSCONFDIR "/tunables.cache" +#endif + +/* Tunable Override Policies. */ +typedef enum { + TOP_DEFAULT = 0, /* let the internal code decide */ + TOP_ALLOW, /* let the environment variable override */ + TOP_STRICT, /* internal code will only allow "stricter" setting */ + TOP_DENY /* no override allowed */ +} TOP; + +struct tunable_entry_int { + struct stringtable_entry *name; + struct stringtable_entry *value; + TOP top; + int tunable_id; + int value_is_negative:1; + int value_was_parsed:1; + unsigned long long value_ull; + signed long long value_sll; + + struct tunable_entry_int *next; +}; + +struct tunable_entry_int *entry_list; +struct tunable_entry_int **entry_list_next = &entry_list; + +/*----------------------------------------------------------------------*/ + +static void parse_tunconf_include (const char *tunconfig_file, unsigned int lineno, + bool do_chroot, const char *pattern, const char *opt_chroot); + +static void +add_tunable (char *line, const char *filename, int lineno) +{ + TOP top = TOP_DEFAULT; + char *name; + char *value; + char *eq; + char *orig_line; + struct tunable_entry_int *entry; + int i, id; + + orig_line = line; + + /* Leading whitespace has already been stripped. */ + + if (*line == '!' || *line == '+' || *line == '-') + { + switch (*line) + { + case '!': + top = TOP_STRICT; + break; + case '+': + top = TOP_ALLOW; + break; + case '-': + top = TOP_DENY; + break; + } + printf("TOP: %d\n", top); + line ++; + while (*line && isspace(*line)) + line ++; + } + + /* NAME now points to the start of the tunable name. */ + name = line; + + /* Look for the '=' separator. */ + eq = strchr (line, '='); + if (eq == NULL) + { + printf("%s:%d: syntax error, line ignored: `%s' (missing '=')\n", + filename, lineno, orig_line); + return; + } + + if (eq == name) + { + printf("%s:%d: syntax error, line ignored: `%s' (missing tunable name)\n", + filename, lineno, orig_line); + return; + } + + /* At this point, EQ actually points to '='. */ + value = eq + 1; + + while (*value && isspace(*value)) + value ++; + + if (*value == 0) + { + printf("%s:%d: syntax error, line ignored: `%s' (missing value)\n", + filename, lineno, orig_line); + return; + } + + /* VALUE now points to the start of the value. */ + + /* Split the string into name and value c-strings. */ + *eq = 0; + /* Trim trailing whitespace off NAME. */ + while (*name && isspace (name[strlen(name)-1])) + name[strlen(name)-1] = 0; + /* Trim trailing whitespace off VALUE. */ + while (*value && isspace (value[strlen(value)-1])) + value[strlen(value)-1] = 0; + + id = -1; + for (i=0; iname = cache_store_string (name); + entry->value = cache_store_string (value); + entry->tunable_id = id; + entry->top = top; + + *entry_list_next = entry; + entry_list_next = & (entry->next); +} + +void +parse_tunconf (const char *filename, int do_chroot, char *opt_chroot) +{ + FILE *file = NULL; + char *line = NULL; + const char *canon; + size_t len = 0; + unsigned int lineno; + + if (do_chroot && opt_chroot) + { + canon = chroot_canon (opt_chroot, filename); + if (canon) + file = fopen (canon, "r"); + else + canon = filename; + } + else + { + canon = filename; + file = fopen (filename, "r"); + } + + if (file == NULL) + { + if (errno != ENOENT) + error (0, errno, _("\ +Warning: ignoring configuration file that cannot be opened: %s"), + canon); + if (canon != filename) + free ((char *) canon); + return; + } + + /* No threads use this stream. */ + __fsetlocking (file, FSETLOCKING_BYCALLER); + + if (canon != filename) + free ((char *) canon); + lineno = 0; + do + { + ssize_t n = getline (&line, &len, file); + if (n < 0) + break; + + ++lineno; + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + /* Because the file format does not know any form of quoting we + can search forward for the next '#' character and if found + make it terminating the line. */ + *strchrnul (line, '#') = '\0'; + + /* Remove leading whitespace. NUL is no whitespace character. */ + char *cp = line; + while (isspace (*cp)) + ++cp; + + /* If the line is blank it is ignored. */ + if (cp[0] == '\0') + continue; + + if (!strncmp (cp, "include", 7) && isblank (cp[7])) + { + char *dir; + cp += 8; + while ((dir = strsep (&cp, " \t")) != NULL) + if (dir[0] != '\0') + parse_tunconf_include (filename, lineno, do_chroot, dir, opt_chroot); + } + else + add_tunable (cp, filename, lineno); + } + while (!feof_unlocked (file)); + + /* Free buffer and close file. */ + free (line); + fclose (file); +} + +/* Handle one word in an `include' line, a glob pattern of additional + config files to read. */ +static void +parse_tunconf_include (const char *tunconfig_file, unsigned int lineno, + bool do_chroot, const char *pattern, const char *opt_chroot) +{ + if (opt_chroot != NULL && pattern[0] != '/') + error (EXIT_FAILURE, 0, + _("need absolute file name for configuration file when using -r")); + + char *copy = NULL; + if (pattern[0] != '/' && strchr (tunconfig_file, '/') != NULL) + { + if (asprintf (©, "%s/%s", dirname (strdupa (tunconfig_file)), + pattern) < 0) + error (EXIT_FAILURE, 0, _("memory exhausted")); + pattern = copy; + } + + glob64_t gl; + int result; + if (do_chroot && opt_chroot) + { + char *canon = chroot_canon (opt_chroot, pattern); + if (canon == NULL) + return; + result = glob64 (canon, 0, NULL, &gl); + free (canon); + } + else + result = glob64 (pattern, 0, NULL, &gl); + + switch (result) + { + case 0: + for (size_t i = 0; i < gl.gl_pathc; ++i) + parse_tunconf (gl.gl_pathv[i], false, NULL); + globfree64 (&gl); + break; + + case GLOB_NOMATCH: + break; + + case GLOB_NOSPACE: + errno = ENOMEM; + /* Fall through. */ + case GLOB_ABORTED: + if (opt_verbose) + error (0, errno, _("%s:%u: cannot read directory %s"), + tunconfig_file, lineno, pattern); + break; + + default: + abort (); + break; + } + + free (copy); +} + +struct tunable_header_cached * +get_tunconf_ext (uint32_t string_table_offset) +{ + struct tunable_entry_int *tei; + struct tunable_header_cached *thc; + size_t count; + size_t size; + + /* First, count the number of entries we have. */ + tei = entry_list; + count = 0; + while (tei != NULL) + { + ++ count; + tei = tei->next; + } + + /* Allocate enough space for the whole cached block. */ + size = sizeof (struct tunable_header_cached) + + sizeof (struct tunable_entry_cached) * count; + thc = (struct tunable_header_cached *) malloc (size); + + if (thc == NULL) + { + error (0, 0, _("Unable to allocate %zu bytes in get_tunable_ext"), size); + return NULL; + } + + /* Now, fill in the structures. */ + + thc->signature = TUNCONF_SIGNATURE; + thc->version = TUNCONF_VERSION; + thc->num_tunables = count; + thc->unused_1 = 0; + + tei = entry_list; + count = 0; + while (tei != NULL) + { + struct tunable_entry_cached *tec; + + tec = & ( thc->tunables[count] ); + + tec->flags = 0; + if (tei->value_was_parsed) + tec->flags |= TUNCONF_FLAG_PARSED; + if (tei->value_is_negative) + tec->flags |= TUNCONF_FLAG_NEGATIVE; + switch (tei->top) + { + case TOP_DEFAULT: + tec->flags |= TUNCONF_OVERRIDE_DEFAULT; + break; + case TOP_ALLOW: + tec->flags |= TUNCONF_OVERRIDE_ALLOW; + break; + case TOP_STRICT: + tec->flags |= TUNCONF_OVERRIDE_STRICTER; + break; + case TOP_DENY: + tec->flags |= TUNCONF_OVERRIDE_DENY; + break; + } + + tec->tunable_id = tei->tunable_id; + tec->name_offset = tei->name->offset + string_table_offset; + tec->value_offset = tei->value->offset + string_table_offset; + tec->flag_offset = 0; + tec->unused_1 = 0; + if (tei->value_is_negative) + tec->parsed_value = (uint64_t) tei->value_sll; + else + tec->parsed_value = (uint64_t) tei->value_ull; + + ++ count; + tei = tei->next; + } + + return thc; +} diff --git a/elf/tunconf.h b/elf/tunconf.h new file mode 100644 index 0000000000..a6c5f0dd9a --- /dev/null +++ b/elf/tunconf.h @@ -0,0 +1,40 @@ +#define TUNCONF_SIGNATURE 0x7c3ba94f +#define TUNCONF_VERSION 0x01000000 + +#define TUNCONF_FLAG_PARSED 0x00000001 +#define TUNCONF_FLAG_NEGATIVE 0x00000002 + +#define TUNCONF_FLAG_OVERRIDABLE 0x0000000C +#define TUNCONF_OVERRIDE_DEFAULT 0x00000000 +#define TUNCONF_OVERRIDE_ALLOW 0x00000004 +#define TUNCONF_OVERRIDE_STRICTER 0x00000008 +#define TUNCONF_OVERRIDE_DENY 0x0000000C + +#define TUNCONF_FLAG_FILTER 0x0000ff00 +#define TUNCONF_FILTER_PERPROC 0x00000100 + +/* An array of [num_tunables] of these follows the below. */ +struct tunable_entry_cached { + uint32_t flags; + uint32_t tunable_id; + uint32_t name_offset; + uint32_t value_offset; + uint32_t flag_offset; + uint32_t unused_1; /* for alignment */ + uint64_t parsed_value; +}; + +/* One of these is at the beginning of the tunable data block. */ +struct tunable_header_cached { + uint32_t signature; + uint32_t version; + uint32_t num_tunables; + uint32_t unused_1; /* for alignment */ + struct tunable_entry_cached tunables[0 /* num_tunables */]; +}; + +void parse_tunconf (const char *filename, int do_chroot, char *opt_chroot); + +struct tunable_header_cached * get_tunconf_ext (uint32_t str_offset); +#define TUNCONF_SIZE(thc_p) (sizeof(struct tunable_header_cached) \ + + thc_p->num_tunables * sizeof (struct tunable_entry_cached)) diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h index 26f0c1e0bd..f438982b6f 100644 --- a/sysdeps/generic/dl-cache.h +++ b/sysdeps/generic/dl-cache.h @@ -220,6 +220,12 @@ enum cache_extension_tag size must be a multiple of 4. */ cache_extension_tag_glibc_hwcaps, + /* Array of system-wide tunable information. + + For this section, 8-byte alignment is required, and the section + size must be a multiple of 8. */ + cache_extension_tag_tunables, + /* Total number of known cache extension tags. */ cache_extension_count }; diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h index 5913bf40fc..da97774793 100644 --- a/sysdeps/generic/ldconfig.h +++ b/sysdeps/generic/ldconfig.h @@ -74,6 +74,8 @@ extern void add_to_cache (const char *path, const char *filename, unsigned int isa_level, struct glibc_hwcaps_subdirectory *); +extern struct stringtable_entry *cache_store_string (const char *string); + extern void init_aux_cache (void); extern void load_aux_cache (const char *aux_cache_name); From patchwork Wed Feb 21 23:47:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 129924 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 2311B4BA2E1B for ; Wed, 11 Feb 2026 05:17:01 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2311B4BA2E1B Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=ZYVhoDo0 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 0B6944BA23C6 for ; Wed, 11 Feb 2026 05:14:19 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0B6944BA23C6 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 0B6944BA23C6 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770786859; cv=none; b=qpRa2tFqjC2ZtNks6dujOWlg03YdYoHIBsZv7iyLheFtt4Efz0C4N2x69P/vPn2XTBdG7QCweCdgonu+vD6L1vJkVk7ewQW3CQf8cDgSD6h648X7jc1u/zX92G4HZL9kcqfZ9o6rOIdbSG1HjfyJHST35DZY/V2pklfwapHQKzQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770786859; c=relaxed/simple; bh=O0grDmxtXdCm5j/ZeGhS1m4yGNItzKeu8SQ8EPA9wL8=; h=DKIM-Signature:To:Message-ID:From:Date:Subject; b=nTdx11wTQ7RyPyUcxMpZE/TuPykQ4dIoHHk9Aek5sAZFEG3gRm+Io/NanR6wMifjKtk+MGC04JFDoef0zpmBYMcWWcW9SxEaMMZeCtp7nMFqQ17m8n5n+h0DGz5v5FlzD99rDk6AooWREH6b0oxZ1ewPzrH5ecPc3+KksZGJCAk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0B6944BA23C6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770786858; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:content-type:content-type:in-reply-to:in-reply-to: references:references; bh=oRcjvt4eFhgR7UfYSMTJvl3PRRG+OxTRjVY2wjiaeqo=; b=ZYVhoDo0up8ZjSb6V2igmuPp0+rebivWhgmgM6Lv2o0I+fOscVXYM4d6LzCkOx+bOnaOUM 2Sx5GZ7REUCpGvfQQ9jc7FxDuu7gsQq6SEAXpCJ8T6vMfUKmdmO1KoG+cg0EQTiZwkVmi3 kq2Q6GZH6ZRQQ3UdHt8Y1Cgy9y9/1uY= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-176-QUgO37vGOuSRXfQl1oC27Q-1; Wed, 11 Feb 2026 00:14:14 -0500 X-MC-Unique: QUgO37vGOuSRXfQl1oC27Q-1 X-Mimecast-MFC-AGG-ID: QUgO37vGOuSRXfQl1oC27Q_1770786853 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 389DF18003F5; Wed, 11 Feb 2026 05:14:13 +0000 (UTC) Received: from greed.delorie.com (unknown [10.22.89.244]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 88F28180066D; Wed, 11 Feb 2026 05:14:12 +0000 (UTC) Received: from greed.delorie.com.redhat.com (localhost [127.0.0.1]) by greed.delorie.com (8.16.1/8.16.1) with ESMTP id 61B5EBsH921669; Wed, 11 Feb 2026 00:14:11 -0500 To: libc-alpha@sourceware.org CC: Sam James , Yury Khrustalev Message-ID: <85ce81b0eecd6ef159d5242e3be759ead16fb8be.1770786739.git.dj@redhat.com> In-Reply-To: References: From: DJ Delorie Date: Wed, 21 Feb 2024 18:47:53 -0500 Subject: [PATCH v5 2/4] Add system-wide tunables: cache ld.so.cache X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 2 X-Mimecast-MFC-PROC-ID: YJKrlv3aHORzDyeCcDi6fTwJ93HjYxLbj7Ps74AmwK8_1770786853 X-Mimecast-Originator: redhat.com Content-type: text/plain; charset=UTF-8 X-Spam-Status: No, score=-5.4 required=5.0 tests=BAYES_00, DATE_IN_PAST_96_XX, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_MAILBOX2, KAM_SHORT, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org The purpose of this change is twofold: 1. The ld.so.cache is cached in memory and only re-read if/when it changes on disk. This allows us to have much more intensive security checks in the future, without impacting performance as much. It also allows for cases where the cache is corrupted - we continue using the last valid one. 2. We break out the load/check logic so that the cache can be loaded independently of the library lookup, such as for code that only needs to look at the extensions. --- elf/dl-cache.c | 254 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 169 insertions(+), 85 deletions(-) diff --git a/elf/dl-cache.c b/elf/dl-cache.c index 9458ffae2a..4612dea835 100644 --- a/elf/dl-cache.c +++ b/elf/dl-cache.c @@ -16,6 +16,7 @@ License along with the GNU C Library; if not, see . */ +#include #include #include #include @@ -25,11 +26,18 @@ #include <_itoa.h> #include #include +#include +#include +#include /* This is the starting address and the size of the mmap()ed file. */ static struct cache_file *cache; static struct cache_file_new *cache_new; static size_t cachesize; +static struct cache_extension_all_loaded ext; + +static struct __stat64_t64 cache_file_time; +static struct __stat64_t64 new_cache_file_time; #ifdef SHARED /* This is used to cache the priorities of glibc-hwcaps @@ -52,6 +60,7 @@ glibc_hwcaps_priorities_free (void) free (glibc_hwcaps_priorities); glibc_hwcaps_priorities = NULL; glibc_hwcaps_priorities_allocated = 0; + glibc_hwcaps_priorities_length = 0; } /* Ordered comparison of a hwcaps string from the cache on the left @@ -83,10 +92,6 @@ glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right) static void glibc_hwcaps_priorities_init (void) { - struct cache_extension_all_loaded ext; - if (!cache_extension_load (cache_new, cache, cachesize, &ext)) - return; - uint32_t length = (ext.sections[cache_extension_tag_glibc_hwcaps].size / sizeof (uint32_t)); if (length > glibc_hwcaps_priorities_allocated) @@ -373,6 +378,159 @@ _dl_cache_libcmp (const char *p1, const char *p2) return *p1 - *p2; } +/* Set the cache back to the "no cache" state, which may include + cleaning up a loaded cache. */ +static void +_dl_maybe_unload_ldsocache (void) +{ + if (cache != NULL) + __munmap (cache, cachesize); + + cache = NULL; + cache_new = NULL; + cachesize = 0; + +#ifdef SHARED + glibc_hwcaps_priorities_free (); +#endif +} + +/* Returns TRUE if for any reason the cache needs to be reloaded + (including, the first time, loaded). */ +static bool +_dl_check_ldsocache_needs_loading (void) +{ + int rv; + static int copy_old_time = 0; + + /* Save the previous stat every time. We only care when this + changes, and we only stat it here, so we can get away with doing + the copy now instead of at every single return statement in this + function. However, we only need to copy it if the previous stat + succeeded. The only way this could be subverted is if the admin + moves the file aside, then moves it back, but CACHE would be set + to NULL in the interim so that would be detected. */ + if (copy_old_time) + cache_file_time = new_cache_file_time; + rv = __fstatat64_time64 (AT_FDCWD, LD_SO_CACHE, &new_cache_file_time, 0); + copy_old_time = (rv >= 0); + + /* No file to load, but there used to be. Assume user intentionally + deleted the cache and act accordingly. */ + if (rv < 0 && cache != NULL) + { + _dl_maybe_unload_ldsocache (); + return false; + } + + /* No file to load and no loaded cache, so nothing to do. */ + if (rv < 0) + return false; + + /* Any file is better than no file (likely the first time + through). */ + if (cache == NULL) + return true; + + /* At this point, NEW_CACHE_FILE_TIME is valid as well as + CACHE_FILE_TIME, so we compare them. We list fields in the order + they're most likely to be different in. */ + return ((new_cache_file_time.st_mtime != cache_file_time.st_mtime) + || (new_cache_file_time.st_ino != cache_file_time.st_ino) + || (new_cache_file_time.st_size != cache_file_time.st_size) + || (new_cache_file_time.st_dev != cache_file_time.st_dev) + ); +} + +/* Attemps to load and validate the cache. On return, CACHE is either + unchanged (still loaded or still not loaded) or valid. */ +static void +_dl_maybe_load_ldsocache (void) +{ + struct cache_file *tmp_cache = NULL; + struct cache_file_new *tmp_cache_new = NULL; + size_t tmp_cachesize = 0; + + /* Read the contents of the file. */ + void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &tmp_cachesize, + PROT_READ); + + /* We can handle three different cache file formats here: + - only the new format + - the old libc5/glibc2.0/2.1 format + - the old format with the new format in it + The following checks if the cache contains any of these formats. */ + if (file != MAP_FAILED && tmp_cachesize > sizeof *cache_new + && memcmp (file, CACHEMAGIC_VERSION_NEW, + sizeof CACHEMAGIC_VERSION_NEW - 1) == 0 + /* Check for corruption, avoiding overflow. */ + && ((tmp_cachesize - sizeof *cache_new) / sizeof (struct file_entry_new) + >= ((struct cache_file_new *) file)->nlibs)) + { + if (! cache_file_new_matches_endian (file)) + { + __munmap (file, tmp_cachesize); + return; + } + + tmp_cache_new = file; + tmp_cache = file; + } + else if (file != MAP_FAILED && cachesize > sizeof *cache + && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0 + /* Check for corruption, avoiding overflow. */ + && ((tmp_cachesize - sizeof *cache) / sizeof (struct file_entry) + >= ((struct cache_file *) file)->nlibs)) + { + size_t offset; + /* Looks ok. */ + tmp_cache = file; + + /* Check for new version. */ + offset = ALIGN_CACHE (sizeof (struct cache_file) + + cache->nlibs * sizeof (struct file_entry)); + + tmp_cache_new = (struct cache_file_new *) ((void *) tmp_cache + offset); + if (tmp_cachesize < (offset + sizeof (struct cache_file_new)) + || memcmp (tmp_cache_new->magic, CACHEMAGIC_VERSION_NEW, + sizeof CACHEMAGIC_VERSION_NEW - 1) != 0) + tmp_cache_new = NULL; + else + { + if (! cache_file_new_matches_endian (tmp_cache_new)) + /* The old-format part of the cache is bogus as well + if the endianness does not match. (But it is + unclear how the new header can be located if the + endianness does not match.) */ + { + __munmap (file, tmp_cachesize); + return; + } + } + } + else + { + if (file != MAP_FAILED) + __munmap (file, tmp_cachesize); + return; + } + + struct cache_extension_all_loaded tmp_ext; + if (!cache_extension_load (tmp_cache_new, tmp_cache, tmp_cachesize, &tmp_ext)) + /* The extension is corrupt, so the cache is corrupt. */ + return; + + /* If we've gotten here, the loaded cache is good and we need to + save it. */ + _dl_maybe_unload_ldsocache (); + cache = tmp_cache; + cache_new = tmp_cache_new; + cachesize = tmp_cachesize; + ext = tmp_ext; + + assert (cache != NULL); +} + /* Look up NAME in ld.so.cache and return the file name stored there, or null if none is found. The cache is loaded if it was not already. If loading @@ -388,81 +546,14 @@ _dl_load_cache_lookup (const char *name) if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE); - if (cache == NULL) - { - /* Read the contents of the file. */ - void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize, - PROT_READ); - - /* We can handle three different cache file formats here: - - only the new format - - the old libc5/glibc2.0/2.1 format - - the old format with the new format in it - The following checks if the cache contains any of these formats. */ - if (file != MAP_FAILED && cachesize > sizeof *cache_new - && memcmp (file, CACHEMAGIC_VERSION_NEW, - sizeof CACHEMAGIC_VERSION_NEW - 1) == 0 - /* Check for corruption, avoiding overflow. */ - && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new) - >= ((struct cache_file_new *) file)->nlibs)) - { - if (! cache_file_new_matches_endian (file)) - { - __munmap (file, cachesize); - file = (void *) -1; - } - cache_new = file; - cache = file; - } - else if (file != MAP_FAILED && cachesize > sizeof *cache - && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0 - /* Check for corruption, avoiding overflow. */ - && ((cachesize - sizeof *cache) / sizeof (struct file_entry) - >= ((struct cache_file *) file)->nlibs)) - { - size_t offset; - /* Looks ok. */ - cache = file; - - /* Check for new version. */ - offset = ALIGN_CACHE (sizeof (struct cache_file) - + cache->nlibs * sizeof (struct file_entry)); - - cache_new = (struct cache_file_new *) ((void *) cache + offset); - if (cachesize < (offset + sizeof (struct cache_file_new)) - || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW, - sizeof CACHEMAGIC_VERSION_NEW - 1) != 0) - cache_new = (void *) -1; - else - { - if (! cache_file_new_matches_endian (cache_new)) - { - /* The old-format part of the cache is bogus as well - if the endianness does not match. (But it is - unclear how the new header can be located if the - endianness does not match.) */ - cache = (void *) -1; - cache_new = (void *) -1; - __munmap (file, cachesize); - } - } - } - else - { - if (file != MAP_FAILED) - __munmap (file, cachesize); - cache = (void *) -1; - } + if (_dl_check_ldsocache_needs_loading ()) + _dl_maybe_load_ldsocache (); - assert (cache != NULL); - } - - if (cache == (void *) -1) - /* Previously looked for the cache file and didn't find it. */ + if (cache == NULL) return NULL; const char *best; - if (cache_new != (void *) -1) + if (cache_new != NULL) { const char *string_table = (const char *) cache_new; best = search_cache (string_table, cachesize, @@ -488,7 +579,7 @@ _dl_load_cache_lookup (const char *name) return NULL; /* The double copy is *required* since malloc may be interposed - and call dlopen itself whose completion would unmap the data + and call dlopen itself whose completion may unmap the data we are accessing. Therefore we must make the copy of the mapping data without using malloc. */ char *temp; @@ -506,14 +597,7 @@ _dl_load_cache_lookup (const char *name) void _dl_unload_cache (void) { - if (cache != NULL && cache != (struct cache_file *) -1) - { - __munmap (cache, cachesize); - cache = NULL; - } -#ifdef SHARED - /* This marks the glibc_hwcaps_priorities array as out-of-date. */ - glibc_hwcaps_priorities_length = 0; -#endif + /* Functionality is no longer needed, but kept for internal ABI for + now. */ } #endif From patchwork Wed Feb 21 23:49:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 129926 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id B78134BA23CD for ; Wed, 11 Feb 2026 05:51:00 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B78134BA23CD Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=PspyKM23 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 51B2B4BA2E13 for ; Wed, 11 Feb 2026 05:50:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 51B2B4BA2E13 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 51B2B4BA2E13 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770789027; cv=none; b=F+0LreThS6fiyCG67y4h12VRHalsp4Iziq4C3XXz2CIfSX+V37obxR/bSilK3atxD0EV6uhnMnepSasefK4Dty9OiPCvOJdf7XZ0KfV/0Mlb8ga0CeXx8RFBS3iCMO+UH9Kg3ou3tPtFYkGV04+6owq0iRJaPdGBt/6vnBeX51A= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770789027; c=relaxed/simple; bh=txpSAilIugyMfzXijxbX69YjNE1nMfqyQupCb4i5NTs=; h=DKIM-Signature:To:Message-ID:From:Date:Subject; b=KCBtTJ+hTJijktgndCn7M0M/ndNsYjDWnABOoTS9yR/0ETEdqHSh1InsNWLI1iNhRVb8YIwHsOi64oFdToQAjleZ7xetxR0bxe7PEHgWY2hgnMCyDIxoGZWKDRSbGPI9a0F/E4WTZOMV8/6MBIwc4Hk5R8nZH0+0l828u7P3pqo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 51B2B4BA2E13 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770789027; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:content-type:content-type:in-reply-to:in-reply-to: references:references; bh=s6UimE50yVf7urbvOELl0okkVyh35mZLt7m7W+zpo3k=; b=PspyKM23kyFZN0VMjLYAVN0dAnaT6CTLfSO9qRKVdwv2VJaKBiIt/H22ZYxo9pXPvfdWuT LTvIEwkmHlp2cmdxW4O7aIF4mow7yveE+/JFJ6e1R03UefDSXpX6ujE/HrpUoGB2rEIe1d brgnacehyuisKKGeHu1FjFVmFrIMlSA= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-156-z-GCdNXtM_eAsAFHPn0FMA-1; Wed, 11 Feb 2026 00:50:23 -0500 X-MC-Unique: z-GCdNXtM_eAsAFHPn0FMA-1 X-Mimecast-MFC-AGG-ID: z-GCdNXtM_eAsAFHPn0FMA_1770789022 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9FAB4195608A; Wed, 11 Feb 2026 05:50:22 +0000 (UTC) Received: from greed.delorie.com (unknown [10.22.89.244]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 49F3A1800465; Wed, 11 Feb 2026 05:50:22 +0000 (UTC) Received: from greed.delorie.com.redhat.com (localhost [127.0.0.1]) by greed.delorie.com (8.16.1/8.16.1) with ESMTP id 61B5oJcF922745; Wed, 11 Feb 2026 00:50:19 -0500 To: libc-alpha@sourceware.org CC: Sam James , Yury Khrustalev Message-ID: <1079748130c0739fb526fc0be3c8690cfb603064.1770786739.git.dj@redhat.com> In-Reply-To: References: From: DJ Delorie Date: Wed, 21 Feb 2024 18:49:50 -0500 Subject: [PATCH v5 3/4] Add system-wide tunables: Apply tunables part X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 2 X-Mimecast-MFC-PROC-ID: Hz_iLnuU2Hrt4XqIHg36Y74zLsQx1MXBHFtx5HxpFFI_1770789022 X-Mimecast-Originator: redhat.com Content-type: text/plain; charset=UTF-8 X-Spam-Status: No, score=-8.2 required=5.0 tests=BAYES_00, DATE_IN_PAST_96_XX, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Load ld.so.cache and fetch the tunables extension. Apply those tunables to the current program. We do not yet apply security policies. --- elf/Makefile | 1 + elf/dl-cache.c | 25 +++++++++++++++++++ elf/dl-tunables.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++ elf/tunconf.h | 3 +++ 4 files changed, 92 insertions(+) diff --git a/elf/Makefile b/elf/Makefile index 42da891f5a..282af88e71 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -91,6 +91,7 @@ dl-routines = \ ifeq (yes,$(use-ldconfig)) dl-routines += dl-cache +CFLAGS-dl-tunables.c += -DUSE_LDCONFIG endif ifeq (yesyes,$(build-shared)$(run-built-tests)) diff --git a/elf/dl-cache.c b/elf/dl-cache.c index 4612dea835..a6b019e46c 100644 --- a/elf/dl-cache.c +++ b/elf/dl-cache.c @@ -29,6 +29,7 @@ #include #include #include +#include "tunconf.h" /* This is the starting address and the size of the mmap()ed file. */ static struct cache_file *cache; @@ -601,3 +602,27 @@ _dl_unload_cache (void) now. */ } #endif + +const struct tunable_header_cached * +_dl_load_cache_tunables (const char **data) +{ + struct cache_extension_all_loaded ext; + + /* This loads the cache (temporary). */ + if (_dl_check_ldsocache_needs_loading ()) + _dl_maybe_load_ldsocache (); + + if (cache_new && cache_new != (void *) -1) + *data = (const char *) cache_new; + else + return NULL; + + if (!cache_extension_load (cache_new, cache, cachesize, &ext)) + return NULL; + + /* Validate length/contents here. */ + if (ext.sections[cache_extension_tag_tunables].size + < sizeof(struct tunable_header_cached)) + return NULL; + return ext.sections[cache_extension_tag_tunables].base; +} diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c index 37ade374c6..83808f75e7 100644 --- a/elf/dl-tunables.c +++ b/elf/dl-tunables.c @@ -36,6 +36,7 @@ #define TUNABLES_INTERNAL 1 #include "dl-tunables.h" +#include "tunconf.h" /* The function might be called before the process is self-relocated. */ static size_t @@ -306,6 +307,68 @@ __tunables_init (char **envp) char *envval = NULL; char **prev_envp = envp; +#if defined(SHARED) && defined (USE_LDCONFIG) + const struct tunable_header_cached *thc; + const char *td; + + thc = _dl_load_cache_tunables (&td); + if (thc != NULL) + { + for (int t = 0; t < thc->num_tunables; ++ t) + { + const struct tunable_entry_cached *tec = &( thc->tunables[t] ); + int tid = tec->tunable_id; + const char *name = td + tec->name_offset; + const char *value = td + tec->value_offset; + + /* Check that we have the correct tunable, and search by + name if needed. We rely on order of operations here to + avoid mis-indexing tunables[]. */ + if (tid < 0 || tid >= tunables_list_size + || strcmp (name, tunable_list[tid].name) != 0) + { + /* It does not, search by name instead. */ + tid = -1; + for (int i = 0; i < tunables_list_size; i++) + { + if (strcmp (name, tunable_list[i].name) == 0) + { + tid = i; + break; + } + } + if (tid == -1) + continue; + } + /* At this point, TID is valid for the tunable we want. See + if the parsed type matches the desired type. */ + + if (tunable_list[tid].type.type_code == TUNABLE_TYPE_STRING) + { + /* This is a memory leak but there's no easy way around + it, as the mapping will go away. */ + tunable_list[tid].val.strval.str = __strdup (value); + tunable_list[tid].val.strval.len = strlen (value); + } + else + { + tunable_val_t tval; + if (tec->flags & TUNCONF_FLAG_PARSED) + { + tval.numval = tec->parsed_value; + do_tunable_update_val (& tunable_list[tid], + &tval, NULL, NULL); + } + else + { + tunable_initialize (& tunable_list[tid], + value, strlen (value)); + } + } + } + } +#endif + /* Ignore tunables for AT_SECURE programs. */ if (__libc_enable_secure) return; diff --git a/elf/tunconf.h b/elf/tunconf.h index a6c5f0dd9a..623fb7546d 100644 --- a/elf/tunconf.h +++ b/elf/tunconf.h @@ -38,3 +38,6 @@ void parse_tunconf (const char *filename, int do_chroot, char *opt_chroot); struct tunable_header_cached * get_tunconf_ext (uint32_t str_offset); #define TUNCONF_SIZE(thc_p) (sizeof(struct tunable_header_cached) \ + thc_p->num_tunables * sizeof (struct tunable_entry_cached)) + +extern const struct tunable_header_cached * +_dl_load_cache_tunables (const char **data); From patchwork Wed Feb 11 04:33:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 129925 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 8C7B94BA2E09 for ; Wed, 11 Feb 2026 05:18:16 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8C7B94BA2E09 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=YC/FwYCa X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id EE0744BA23D7 for ; Wed, 11 Feb 2026 05:15:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EE0744BA23D7 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org EE0744BA23D7 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770786905; cv=none; b=p27EQwAnQdubBh41FViu3M9G+UfjQFvMa6UZyJ3JeGy96NSVAU9zxdmZLafbBRYWav7v1UWJm/V5yNqrb9Iev2X085hIKntZ14Ti5FNSqcdC3EgyfVx4bAxYdsRWbx9Og57hT8gAwo8gcBq/6zRtmNhZbd7vDQ0qeYLro1SLxns= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770786905; c=relaxed/simple; bh=SHh8B/4rdM3y/5Ixif0IdUrlM0mXIcfm2TunOcuNBKw=; h=DKIM-Signature:To:Message-ID:From:Date:Subject; b=HyyNe9tazyqowCeo6smMzQE+hyWJLXUxfKASXJEQnBAMWCo3Y9GjOYzaqS31Kx7YdDH55ONCaNOoOmeQJiXk1iXWo/Ziyvowk5AmlLEtiTo3/6hPU0DPbTrcXxfxpUmgC1WEiRRa2xb+0CQKoK4vwYR/1T1DiCa5Ax/5nFXzKG4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EE0744BA23D7 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770786904; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:content-type:content-type:in-reply-to:in-reply-to: references:references; bh=CYBlDqGZMUFRfS/peYnRDaHKOp7JnvTbTMOm1krYChs=; b=YC/FwYCaDXnYQwA122q65QI4sp++6UDuAAbm5yW8iCYTDyDL1/hDrLQyDObWWvN7/7ixfx OpJ+5Wpft0e5sQ2a+jkYIwREgzP6O+HQ5Safz0Mnr9WI6IX0ky/sWrQ4GJEYEcpvalKX/i j27LcfcX0gBNVJJFN4EuWkF3vSJopRQ= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-544-tbb50JNJMAydDlOBUwkElw-1; Wed, 11 Feb 2026 00:15:01 -0500 X-MC-Unique: tbb50JNJMAydDlOBUwkElw-1 X-Mimecast-MFC-AGG-ID: tbb50JNJMAydDlOBUwkElw_1770786900 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E78BD1956089; Wed, 11 Feb 2026 05:14:59 +0000 (UTC) Received: from greed.delorie.com (unknown [10.22.89.244]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 533591956053; Wed, 11 Feb 2026 05:14:59 +0000 (UTC) Received: from greed.delorie.com.redhat.com (localhost [127.0.0.1]) by greed.delorie.com (8.16.1/8.16.1) with ESMTP id 61B5EviS921696; Wed, 11 Feb 2026 00:14:57 -0500 To: libc-alpha@sourceware.org CC: Sam James , Yury Khrustalev Message-ID: In-Reply-To: References: From: DJ Delorie Date: Tue, 10 Feb 2026 23:33:51 -0500 Subject: [PATCH v5 4/4] Add system-wide tunables: Filters X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 2 X-Mimecast-MFC-PROC-ID: LFdvci00R2v7wWaBnEg-HvCSWX74eeFCej18-8H3JlQ_1770786900 X-Mimecast-Originator: redhat.com Content-type: text/plain; charset=UTF-8 X-Spam-Status: No, score=-10.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Add support for [proc:*] syntax where * matches /proc/self/exe (fallback: argv[0]). Tunables after such a line are limited to matching processes. Note that this filter is reset when including a file or at end of file. If the filename starts with a slash (example: [proc:/bin/foo]) the full path must match. If not (example: [proc:foo]) the basename is matched. --- csu/libc-start.c | 2 +- elf/Makefile | 4 + elf/cache.c | 3 +- elf/dl-tunables.c | 68 ++++++++++++++++- elf/dl-tunables.h | 2 +- elf/tst-tunconf1.c | 36 +++++++++ elf/tst-tunconf1.root/etc/tunables.conf | 6 ++ elf/tst-tunconf1.root/ldconfig.run | 0 elf/tst-tunconf1.root/postclean.req | 0 elf/tunconf.c | 97 ++++++++++++++++++++++++- sysdeps/mach/hurd/dl-sysdep.c | 2 +- sysdeps/unix/sysv/linux/dl-sysdep.c | 2 +- 12 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 elf/tst-tunconf1.c create mode 100644 elf/tst-tunconf1.root/etc/tunables.conf create mode 100644 elf/tst-tunconf1.root/ldconfig.run create mode 100644 elf/tst-tunconf1.root/postclean.req diff --git a/csu/libc-start.c b/csu/libc-start.c index 1c58561bce..ae36170045 100644 --- a/csu/libc-start.c +++ b/csu/libc-start.c @@ -264,7 +264,7 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), _dl_aux_init (auxvec); # endif - __tunables_init (__environ); + __tunables_init (__environ, argv); ARCH_INIT_CPU_FEATURES (); diff --git a/elf/Makefile b/elf/Makefile index 282af88e71..b407e9e761 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -329,6 +329,7 @@ tests-internal := \ $(tests-static-internal) \ tst-tls1 \ tst-tls_tp_offset \ + tst-tunconf1 \ # tests-internal tests-static := $(tests-static-normal) $(tests-static-internal) @@ -339,6 +340,8 @@ tests-static += \ tst-tls9-static \ # tests-static +tst-tunconf1-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=5 + static-dlopen-environment = \ LD_LIBRARY_PATH=$(ld-library-path):$(common-objpfx)dlfcn tst-tls9-static-ENV = $(static-dlopen-environment) @@ -568,6 +571,7 @@ tests-container += \ tst-pldd \ tst-preload-pthread-libc \ tst-rootdir \ + tst-tunconf1 \ # tests-container test-srcs = \ diff --git a/elf/cache.c b/elf/cache.c index e798f7e2d8..2809f255f5 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -300,9 +300,10 @@ print_extensions (struct cache_extension_all_loaded *ext, thc->signature, thc->version, thc->num_tunables); for (i = 0; i < count; ++ i) { - printf(" [%d] %s : %s [flags 0x%08x", + printf(" [%d] %s (%d) : %s [flags 0x%08x", i, cache_data + tec[i].name_offset, + tec[i].tunable_id, cache_data + tec[i].value_offset, tec[i].flags); if (tec[i].flag_offset != 0) diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c index 83808f75e7..4637f47875 100644 --- a/elf/dl-tunables.c +++ b/elf/dl-tunables.c @@ -301,13 +301,22 @@ parse_tunables (const char *valstring) ENV_ALIAS to find values. Later we will also use the tunable names to find values. */ void -__tunables_init (char **envp) +__tunables_init (char **envp, char **argv) { char *envname = NULL; char *envval = NULL; char **prev_envp = envp; #if defined(SHARED) && defined (USE_LDCONFIG) + const char *prog_name = (argv && argv[0]) ? argv[0] : ""; + int prog_name_len = -1; + const char *base_name = NULL; + int base_name_len = -1; +#ifdef PATH_MAX + char exebuf[PATH_MAX]; +#else + char exebuf[256]; +#endif const struct tunable_header_cached *thc; const char *td; @@ -340,9 +349,58 @@ __tunables_init (char **envp) if (tid == -1) continue; } - /* At this point, TID is valid for the tunable we want. See - if the parsed type matches the desired type. */ - + /* At this point, TID is valid for the tunable we want. */ + + /* Apply selected filter, if any. */ + switch (tec->flags & TUNCONF_FLAG_FILTER) { + case TUNCONF_FILTER_PERPROC: + /* Perform one-time calculations that aren't needed if we + don't use this filter. */ + if (prog_name_len == -1) + { + ssize_t n = readlink ("/proc/self/exe", + exebuf, sizeof (exebuf) - 1); + if (n > 0 && n < sizeof(exebuf)-1) + { + /* If /proc/self/exe exists and we can read it, + it's more reliable than argv[] so use it. */ + exebuf[n] = '\0'; + prog_name = exebuf; + } + if (prog_name != NULL) + { + const char *slash = NULL, *cp; + for (cp = prog_name; *cp; ++ cp) + if (*cp == '/') + slash = cp; + if (slash) + base_name = slash + 1; + else + base_name = prog_name; + prog_name_len = strlen (prog_name); + base_name_len = strlen (base_name); + } + } + /* prog_name and the cached string are both NUL terminated. */ + if (prog_name) + { + if (((const char *)(td + tec->flag_offset))[0] == '/') + { + if (memcmp (prog_name, td + tec->flag_offset, prog_name_len) != 0) + goto skip_due_to_filter; + } + else + { + if (memcmp (base_name, td + tec->flag_offset, base_name_len) != 0) + goto skip_due_to_filter; + } + } + break; + default: + break; + } + + /* See if the parsed type matches the desired type. */ if (tunable_list[tid].type.type_code == TUNABLE_TYPE_STRING) { /* This is a memory leak but there's no easy way around @@ -365,6 +423,8 @@ __tunables_init (char **envp) value, strlen (value)); } } + + skip_due_to_filter: } } #endif diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h index 45aeed47bc..3f34329614 100644 --- a/elf/dl-tunables.h +++ b/elf/dl-tunables.h @@ -47,7 +47,7 @@ typedef void (*tunable_callback_t) (tunable_val_t *); #include "dl-tunable-list.h" -extern void __tunables_init (char **); +extern void __tunables_init (char **, char **); extern void __tunables_print (void); extern bool __tunable_is_initialized (tunable_id_t); extern void __tunable_get_val (tunable_id_t, void *, tunable_callback_t); diff --git a/elf/tst-tunconf1.c b/elf/tst-tunconf1.c new file mode 100644 index 0000000000..c95a7cb8ba --- /dev/null +++ b/elf/tst-tunconf1.c @@ -0,0 +1,36 @@ +/* Test that the tunables cache can override env vars. + Copyright (C) 2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +#include "dl-tunables.h" + +static int +do_test (void) +{ + size_t tcache_count = TUNABLE_GET_FULL (glibc, malloc, tcache_count, size_t, NULL); + size_t tcache_max = TUNABLE_GET_FULL (glibc, malloc, tcache_max, size_t, NULL); + printf("tcache count is %ld (should be 5, from env)\n", (long)tcache_count); + TEST_COMPARE ((long)tcache_count, 5); + printf("tcache max is %ld (should be 4, from /etc)\n", (long)tcache_max); + TEST_COMPARE ((long)tcache_max, 4); + return 0; +} + +#include diff --git a/elf/tst-tunconf1.root/etc/tunables.conf b/elf/tst-tunconf1.root/etc/tunables.conf new file mode 100644 index 0000000000..3c7b33c399 --- /dev/null +++ b/elf/tst-tunconf1.root/etc/tunables.conf @@ -0,0 +1,6 @@ +glibc.malloc.tcache_max=6 +glibc.malloc.tcache_count=3 +[proc:/bin/ls] +glibc.malloc.tcache_max=7 +[proc:tst-tunconf1] +glibc.malloc.tcache_max=4 diff --git a/elf/tst-tunconf1.root/ldconfig.run b/elf/tst-tunconf1.root/ldconfig.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/elf/tst-tunconf1.root/postclean.req b/elf/tst-tunconf1.root/postclean.req new file mode 100644 index 0000000000..e69de29bb2 diff --git a/elf/tunconf.c b/elf/tunconf.c index ea547e95d5..cb26d47816 100644 --- a/elf/tunconf.c +++ b/elf/tunconf.c @@ -65,12 +65,14 @@ typedef enum { struct tunable_entry_int { struct stringtable_entry *name; struct stringtable_entry *value; + struct stringtable_entry *filter; TOP top; int tunable_id; int value_is_negative:1; int value_was_parsed:1; unsigned long long value_ull; signed long long value_sll; + long filter_flags; struct tunable_entry_int *next; }; @@ -78,11 +80,80 @@ struct tunable_entry_int { struct tunable_entry_int *entry_list; struct tunable_entry_int **entry_list_next = &entry_list; +static int filter_flags = 0; +static char *filter_string = NULL; + /*----------------------------------------------------------------------*/ static void parse_tunconf_include (const char *tunconfig_file, unsigned int lineno, bool do_chroot, const char *pattern, const char *opt_chroot); + +static void +clear_filter (void) +{ + free (filter_string); + filter_string = NULL; + filter_flags = 0; +} + +/* Filters are lines the are bracketed, like + [prog:foo] +*/ +static void +parse_filter (char *line, const char *filename, int lineno) +{ + const char *colon = NULL; + const char *right_bracket = NULL; + const char *cp; + + for (cp=line; *cp != 0; cp++) + { + if (*cp == ':') + colon = cp; + if (*cp == ']') + { + right_bracket = cp; + break; + } + } + /* Special case: [] means "no filter" */ + if (right_bracket != NULL && right_bracket == line + 1) + { + clear_filter (); + return; + } + if (colon == NULL) + { + printf("%s:%d: syntax error, filter line ignored: `%s' (missing ':')\n", + filename, lineno, line); + return; + } + if (right_bracket == NULL) + { + printf("%s:%d: syntax error, filter line ignored: `%s' (missing ']')\n", + filename, lineno, line); + return; + } + + if (filter_string != NULL) + { + clear_filter (); + } + + if (memcmp ("proc", line + 1, colon - line - 1) == 0) + { + filter_string = (char *) malloc (right_bracket - colon); + memcpy (filter_string, colon + 1, right_bracket - colon - 1); + filter_string [right_bracket - colon] = 0; + filter_flags = TUNCONF_FILTER_PERPROC; + } + + else + printf("%s:%d: unrecognized filter `%.*s', ignored\n", filename, lineno, (int)(colon - line - 1), line + 1); +} + + static void add_tunable (char *line, const char *filename, int lineno) { @@ -98,7 +169,7 @@ add_tunable (char *line, const char *filename, int lineno) /* Leading whitespace has already been stripped. */ - if (*line == '!' || *line == '+' || *line == '-') + if (*line == '!' || *line == '+' || *line == '-' || *line == '[') { switch (*line) { @@ -111,8 +182,10 @@ add_tunable (char *line, const char *filename, int lineno) case '-': top = TOP_DENY; break; + case '[': + parse_filter (line, filename, lineno); + return; } - printf("TOP: %d\n", top); line ++; while (*line && isspace(*line)) line ++; @@ -175,6 +248,12 @@ add_tunable (char *line, const char *filename, int lineno) entry->tunable_id = id; entry->top = top; + if (filter_flags) + { + entry->filter_flags = filter_flags; + entry->filter = cache_store_string (filter_string); + } + *entry_list_next = entry; entry_list_next = & (entry->next); } @@ -188,6 +267,9 @@ parse_tunconf (const char *filename, int do_chroot, char *opt_chroot) size_t len = 0; unsigned int lineno; + /* Filters do not live across file boundaries. */ + clear_filter (); + if (do_chroot && opt_chroot) { canon = chroot_canon (opt_chroot, filename); @@ -259,6 +341,7 @@ Warning: ignoring configuration file that cannot be opened: %s"), /* Free buffer and close file. */ free (line); fclose (file); + clear_filter (); } /* Handle one word in an `include' line, a glob pattern of additional @@ -388,7 +471,15 @@ get_tunconf_ext (uint32_t string_table_offset) tec->tunable_id = tei->tunable_id; tec->name_offset = tei->name->offset + string_table_offset; tec->value_offset = tei->value->offset + string_table_offset; - tec->flag_offset = 0; + + if (tei->filter_flags != 0) + { + tec->flag_offset = tei->filter->offset + string_table_offset; + tec->flags |= tei->filter_flags; + } + else + tec->flag_offset = 0; + tec->unused_1 = 0; if (tei->value_is_negative) tec->parsed_value = (uint64_t) tei->value_sll; diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c index 0e348d6440..fe6d453756 100644 --- a/sysdeps/mach/hurd/dl-sysdep.c +++ b/sysdeps/mach/hurd/dl-sysdep.c @@ -98,7 +98,7 @@ _dl_sysdep_start (void **start_argptr, __libc_enable_secure = _dl_hurd_data->flags & EXEC_SECURE; - __tunables_init (_environ); + __tunables_init (_environ, _dl_argv); /* Initialize DSO sorting algorithm after tunables. */ _dl_sort_maps_init (); diff --git a/sysdeps/unix/sysv/linux/dl-sysdep.c b/sysdeps/unix/sysv/linux/dl-sysdep.c index cb1f94ee23..c2701f274c 100644 --- a/sysdeps/unix/sysv/linux/dl-sysdep.c +++ b/sysdeps/unix/sysv/linux/dl-sysdep.c @@ -107,7 +107,7 @@ _dl_sysdep_start (void **start_argptr, dl_hwcap_check (); - __tunables_init (_environ); + __tunables_init (_environ, (char **) (start_argptr + 1)); /* Initialize DSO sorting algorithm after tunables. */ _dl_sort_maps_init ();