[v6,4/4] Add system-wide tunables: Filters
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
success
|
Test passed
|
| redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
Commit Message
Add support for [proc:*] syntax where * matches /proc/self/exe
(fallback: argv[0] unless AT_SECURE). 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.
Add support for filtering out AT_SECURE or non-AT_SECURE binaries:
$glibc.only-for.unsecure-binaries=1
@glibc.only-for.secure-binaries=1
---
csu/libc-start.c | 2 +-
elf/Makefile | 4 +
elf/cache.c | 3 +-
elf/dl-tunables.c | 80 +++++++++++++++-
elf/dl-tunables.h | 2 +-
elf/ldconfig-parse.c | 6 +-
elf/ldconfig.c | 3 +
elf/tst-tunconf1.c | 36 +++++++
elf/tst-tunconf1.root/etc/tunables.conf | 15 +++
elf/tst-tunconf1.root/ldconfig.run | 0
elf/tst-tunconf1.root/postclean.req | 0
elf/tunconf.c | 122 +++++++++++++++++++++++-
elf/tunconf.h | 3 +
sysdeps/mach/hurd/dl-sysdep.c | 2 +-
sysdeps/unix/sysv/linux/dl-sysdep.c | 2 +-
15 files changed, 265 insertions(+), 15 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
Comments
Hello DJ,
On Mon, Mar 16, 2026 at 09:39:39PM -0400, DJ Delorie wrote:
>
> Add support for [proc:*] syntax where * matches /proc/self/exe
> (fallback: argv[0] unless AT_SECURE). 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.
>
> Add support for filtering out AT_SECURE or non-AT_SECURE binaries:
>
> $glibc.only-for.unsecure-binaries=1
> @glibc.only-for.secure-binaries=1
We probably need a bit information about /etc/tunables.conf in the
manual.
>
> ...
>
> 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);
Nit: For some deeply intrinsic reason I prefer argv to go first.
>
> ARCH_INIT_CPU_FEATURES ();
>
> diff --git a/elf/Makefile b/elf/Makefile
> index 5398fe0d2c..4fbd03cefc 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -323,6 +323,7 @@ tests-internal := \
> $(tests-static-internal) \
> tst-tls1 \
> tst-tls_tp_offset \
> + tst-tunconf1 \
> # tests-internal
>
> tests-static := $(tests-static-normal) $(tests-static-internal)
> @@ -333,6 +334,8 @@ tests-static += \
> tst-tls9-static \
> # tests-static
>
> +tst-tunconf1-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=5
If I add glibc.malloc.tcache_max=42 to this line, the test fails.
It looks like the environment variable overrides tunables.conf,
is this intentional?
In this test we probably should have the same tunable in both
environment and tunables.conf, and the test should check that
whatever has higher priority applies.
>
> ...
>
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 29fcd4504b..acee6eb872 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -291,13 +291,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)
>
> ...
>
> +
> + /* 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);
Do we have to do this inside the for loop?
Thanks,
Yury
Yury Khrustalev <yury.khrustalev@arm.com> writes:
>> $glibc.only-for.unsecure-binaries=1
>> @glibc.only-for.secure-binaries=1
>
> We probably need a bit information about /etc/tunables.conf in the
> manual.
Agreed. Writing up all the documentation is the next task on my list,
but I'm trying to avoid this patch rotting for a couple more years while
waiting for every little feature to be haggled over, reviewed, and
committed. This patch set is step 1 ;-)
>> - __tunables_init (__environ);
>> + __tunables_init (__environ, argv);
>
> Nit: For some deeply intrinsic reason I prefer argv to go first.
I won't put in argc even if you ask ;-)
>> +tst-tunconf1-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=5
>
> If I add glibc.malloc.tcache_max=42 to this line, the test fails.
> It looks like the environment variable overrides tunables.conf,
> is this intentional?
Yes! At this point, all the security implementation is missing (aside
from AT_SECURE, because that was easy). The "problem" with the security
model I chose is that each tunable needs some inherent logic about
what's "more secure". For numeric values, is a bigger one more secure,
or a smaller one? What about hwcaps? Is adding a hwcap more secure, or
removing one? Does it depend on which hwcap?
I put the syntax in because it affects the file format and I didn't want
to have to change that later. I might end up skipping the "more secure"
implementation and just do the "override or not" options, but even the,
what does "do not override" mean for hwcaps? Is it per-cap or the whole
string?
Either way, it (like the docs and man pages) was intentionally left as a
"next step".
>> + /* 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);
>
> Do we have to do this inside the for loop?
We only do it once, and only if the tunable has that filter. I assume
most systems will not even have a tunables.conf, much less a per-program
filter, so I didn't want to do file I/O if not needed.
On Wed, Mar 25, 2026 at 02:36:13PM -0400, DJ Delorie wrote:
> Yury Khrustalev <yury.khrustalev@arm.com> writes:
> >> $glibc.only-for.unsecure-binaries=1
> >> @glibc.only-for.secure-binaries=1
> >
> > We probably need a bit information about /etc/tunables.conf in the
> > manual.
>
> Agreed. Writing up all the documentation is the next task on my list,
> but I'm trying to avoid this patch rotting for a couple more years while
> waiting for every little feature to be haggled over, reviewed, and
> committed. This patch set is step 1 ;-)
Agreed.
>
> >> - __tunables_init (__environ);
> >> + __tunables_init (__environ, argv);
> >
> > Nit: For some deeply intrinsic reason I prefer argv to go first.
>
> I won't put in argc even if you ask ;-)
>
> >> +tst-tunconf1-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=5
> >
> > If I add glibc.malloc.tcache_max=42 to this line, the test fails.
> > It looks like the environment variable overrides tunables.conf,
> > is this intentional?
>
> Yes! At this point, all the security implementation is missing (aside
> from AT_SECURE, because that was easy).
Noted.
> The "problem" with the security
> model I chose is that each tunable needs some inherent logic about
> what's "more secure". For numeric values, is a bigger one more secure,
> or a smaller one? What about hwcaps? Is adding a hwcap more secure, or
> removing one? Does it depend on which hwcap?
OK, so I prefer to think about this in a different way. Whatever mechanism
we implement here, we should not have to understand the details of each
tunable. This would be Sisyphean effort. Some tunables are not supposed
to have "monotonicity" of their values.
We're adding 3rd way to provide a value for a tunable. So far we have:
0) Default values
1) Values from the GLIBC_TUNABLES environment variable.
Now we will have a value that comes from /etc/tunables.conf.
So, regardless of what each value actually means, we should decide
which source of this value is to be used where there are more than
one value provided for this tunable.
For example, right now, the value from GLIBC_TUNABLES overrides
the default value. Now, which one has higher priority, env var or
system-wide config?
I have an opinion here but I don't want to impose it :)
> I put the syntax in because it affects the file format and I didn't want
> to have to change that later. I might end up skipping the "more secure"
> implementation and just do the "override or not" options, but even the,
> what does "do not override" mean for hwcaps? Is it per-cap or the whole
> string?
>
> Either way, it (like the docs and man pages) was intentionally left as a
> "next step".
I think we should decide on precedence at this point. Since it's not
dependent on which value is "better" or "more secure", we should be able
to do this.
>
> >> + /* 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);
> >
> > Do we have to do this inside the for loop?
>
> We only do it once, and only if the tunable has that filter. I assume
> most systems will not even have a tunables.conf, much less a per-program
> filter, so I didn't want to do file I/O if not needed.
Ah, thanks for clarification, I haven't followed the code properly. What
you do makes sense.
Cheers,
Yury
Yury Khrustalev <yury.khrustalev@arm.com> writes:
> For example, right now, the value from GLIBC_TUNABLES overrides
> the default value. Now, which one has higher priority, env var or
> system-wide config?
>
> I have an opinion here but I don't want to impose it :)
I think the config needs to be able to choose from these (ignoring the
defaults, for now, because we *always* have defaults):
1) system-wide
2) env var
1) system-wide only
1) env var only (by not mentioning it in tunables.conf)
The only other non-Sisyphean option would be:
1) system-wide
2) env var if numerically larger (closer to +Inf)
1) system-wide
2) env var if numerically smaller (closer to -Inf)
but that obviously won't include non-numeric tunables.
On Thu, Mar 26, 2026 at 01:38:50PM -0400, DJ Delorie wrote:
>
> Yury Khrustalev <yury.khrustalev@arm.com> writes:
> > For example, right now, the value from GLIBC_TUNABLES overrides
> > the default value. Now, which one has higher priority, env var or
> > system-wide config?
> >
> > I have an opinion here but I don't want to impose it :)
>
> I think the config needs to be able to choose from these (ignoring the
> defaults, for now, because we *always* have defaults):
>
> 1) system-wide
> 2) env var
>
> 1) system-wide only
>
> 1) env var only (by not mentioning it in tunables.conf)
>
> The only other non-Sisyphean option would be:
>
> 1) system-wide
> 2) env var if numerically larger (closer to +Inf)
We should not take into account values of tunables. This would be a
mistake: like I said, some tunables don't have any monotonicity of
their values.
> 1) system-wide
> 2) env var if numerically smaller (closer to -Inf)
Ditto
>
> but that obviously won't include non-numeric tunables.
>
One more reason not to consider the values of tunables.
I think we should use the following approach:
1) Use default if not overridden (via env var or tunables.conf).
2) Env var overrides tunables.conf unless tunables.conf has special
"final" marker for this tunable.
Plain and simple. Again, actual values of the tunables are irrelevant.
Yury Khrustalev <yury.khrustalev@arm.com> writes:
> We should not take into account values of tunables. This would be a
> mistake: like I said, some tunables don't have any monotonicity of
> their values.
True, but some tunables *do* and why not let the admin have the option?
Consider:
* Admin limits the number of malloc heaps to 16 because glibc
autodetects the number of CPUs wrong.
* User further limits it to 1 to avoid heap-related fragmentation.
On Mon, Mar 30, 2026 at 12:03:36PM -0400, DJ Delorie wrote:
> Yury Khrustalev <yury.khrustalev@arm.com> writes:
> > We should not take into account values of tunables. This would be a
> > mistake: like I said, some tunables don't have any monotonicity of
> > their values.
>
> True, but some tunables *do* and why not let the admin have the option?
Let's do it step by step. What I propose will cover all possible
scenarios: admin enforces a value (1), recommends a value (2), doesn't
care (3).
>
> Consider:
>
> * Admin limits the number of malloc heaps to 16 because glibc
> autodetects the number of CPUs wrong.
This will complicate things and we would have to assess how useful this
use case is going to be before we try to implement it.
Plus, we probably should avoid making tunables.conf Turing-complete,
otherwise it will become a vulnerability of its own :) If you allow to
narrow down the accepted range, you'd have to support both upper and
lower boundary. This is doable, but I'd rather add this in another
patch, but only if there is some need for this.
>
> * User further limits it to 1 to avoid heap-related fragmentation.
Also, as far as setting tunables is concerned, there is no notion of
"limiting" anything: we set a value if we want to use a non-default one.
In a nutshell, let's keep it simple. We could try adding scripting
support, GPU acceleration, and distributed compute a few decayed protons
later :)
Yury Khrustalev <yury.khrustalev@arm.com> writes:
> Let's do it step by step. What I propose will cover all possible
> scenarios: admin enforces a value (1), recommends a value (2), doesn't
> care (3).
Sure, I'm ok with only implementing the "overridable or not" feature at
first. I'd like to leave the two-bit field to allow for two other
options later, though. I think we can spare a bit for unseen future
needs, and I still think allowing an admin to permit bigger/smaller
values, has some value.
>> Consider:
>>
>> * Admin limits the number of malloc heaps to 16 because glibc
>> autodetects the number of CPUs wrong.
>
> This will complicate things and we would have to assess how useful this
> use case is going to be before we try to implement it.
I wasn't trying to justify the use case, just trying to demonstrate why
a bigger/smaller restriction might be useful to an admin.
> Plus, we probably should avoid making tunables.conf Turing-complete,
I offered to put a full LR(1) language in there with a byte code
interpreter, but Florian quickly stopped me ;-)
(I've done it before. It's not that difficult. Let me whip up a
patch... no? are you sure? well, ok... ;)
> otherwise it will become a vulnerability of its own :) If you allow to
> narrow down the accepted range, you'd have to support both upper and
> lower boundary. This is doable, but I'd rather add this in another
> patch, but only if there is some need for this.
Later is OK. Simple is good, I don't want to do a range, just
increase/decrease.
>> * User further limits it to 1 to avoid heap-related fragmentation.
>
> Also, as far as setting tunables is concerned, there is no notion of
> "limiting" anything: we set a value if we want to use a non-default one.
I was referring to the effect of the tunable. That tunable
(malloc.arena_max) is a maximum, so it's action is to limit.
@@ -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 ();
@@ -323,6 +323,7 @@ tests-internal := \
$(tests-static-internal) \
tst-tls1 \
tst-tls_tp_offset \
+ tst-tunconf1 \
# tests-internal
tests-static := $(tests-static-normal) $(tests-static-internal)
@@ -333,6 +334,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)
@@ -563,6 +566,7 @@ tests-container += \
tst-pldd \
tst-preload-pthread-libc \
tst-rootdir \
+ tst-tunconf1 \
# tests-container
test-srcs = \
@@ -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)
@@ -291,13 +291,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;
@@ -330,9 +339,70 @@ __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. */
+
+ if (tec->flags & TUNCONF_EXCLUDE_SECURE && __libc_enable_secure)
+ goto skip_due_to_filter;
+ if (tec->flags & TUNCONF_EXCLUDE_UNSECURE && !__libc_enable_secure)
+ goto skip_due_to_filter;
+
+ /* 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;
+ }
+ else if (__libc_enable_secure)
+ prog_name = NULL;
+ 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;
+ }
+ }
+ else
+ /* Program is AT_SECURE but the only source of program
+ name is argv[0], which is not secure, so we do not
+ match any name-based filter. */
+ 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
@@ -355,6 +425,8 @@ __tunables_init (char **envp)
value, strlen (value));
}
}
+
+ skip_due_to_filter:;
}
}
#endif /* defined(SHARED) && defined (USE_LDCONFIG) */
@@ -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);
@@ -47,8 +47,10 @@ ldconfig_parse_config_1 (const char *filename, bool do_chroot,
opt_chroot - If non-NULL, all paths are relative to this.
- callback - for each non-blank line in the file, this function is called
- with the line and it's location.
+ callback - for each non-blank line in the file, this function is
+ called with the line and it's location. Will also be called
+ with a NULL line at the start and end of each file, for
+ file-scoped config items.
*/
void
@@ -435,6 +435,9 @@ add_dir_1 (const char *line, const char *from_file, int from_line)
static void
add_dir_callback (char *line, const char *from_file, int from_line)
{
+ /* Denotes file boundaries. Not needed here. */
+ if (line == NULL)
+ return;
if (!strncasecmp (line, "hwcap", 5) && isblank (line[5]))
error (0, 0, _("%s:%u: hwcap directive ignored"), from_file, from_line);
else
new file mode 100644
@@ -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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <support/check.h>
+
+#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 <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,15 @@
+# These test the parser for both the overridability characters as well as
+# tunables that either never exist, or only exist on some platforms.
+!glibc.foo=19
+-glibc.cpu.cached_memopt=1
++glibc.cpu.hwcaps=some,random,string
+@glibc.test_secure=1
+$glibc.test_unsecure=1
+
+# These are checked inside the test case
+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
new file mode 100644
new file mode 100644
@@ -66,12 +66,16 @@ typedef enum {
struct tunable_entry_int {
struct stringtable_entry *name;
struct stringtable_entry *value;
+ struct stringtable_entry *filter;
TOP top;
+ bool exclude_secure:1;
+ bool exclude_unsecure:1;
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;
};
@@ -79,8 +83,76 @@ 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
+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)
{
@@ -91,12 +163,20 @@ add_tunable (char *line, const char *filename, int lineno)
char *orig_line;
struct tunable_entry_int *entry;
int i, id;
+ bool exclude_secure = 0, exclude_unsecure = 0;
+
+ /* Denotes file boundaries. */
+ if (line == NULL)
+ {
+ clear_filter();
+ return;
+ }
orig_line = line;
/* Leading whitespace has already been stripped. */
- if (*line == '!' || *line == '+' || *line == '-')
+ while (*line)
{
switch (*line)
{
@@ -109,11 +189,25 @@ add_tunable (char *line, const char *filename, int lineno)
case '-':
top = TOP_DENY;
break;
+ case '@':
+ exclude_unsecure = 1;
+ break;
+ case '$':
+ exclude_secure = 1;
+ break;
+ case '[':
+ parse_filter (line, filename, lineno);
+ return;
+ case ' ':
+ case '\t':
+ break;
+
+ default:
+ goto done;
}
line ++;
- while (*line && isspace(*line))
- line ++;
}
+ done:
/* NAME now points to the start of the tunable name. */
name = line;
@@ -174,6 +268,14 @@ add_tunable (char *line, const char *filename, int lineno)
entry->value = cache_store_string (value);
entry->tunable_id = id;
entry->top = top;
+ entry->exclude_secure = exclude_secure;
+ entry->exclude_unsecure = exclude_unsecure;
+
+ if (filter_flags)
+ {
+ entry->filter_flags = filter_flags;
+ entry->filter = cache_store_string (filter_string);
+ }
*entry_list_next = entry;
entry_list_next = & (entry->next);
@@ -248,11 +350,23 @@ get_tunconf_ext (uint32_t string_table_offset)
tec->flags |= TUNCONF_OVERRIDE_DENY;
break;
}
+ if (tei->exclude_secure)
+ tec->flags |= TUNCONF_EXCLUDE_SECURE;
+ if (tei->exclude_unsecure)
+ tec->flags |= TUNCONF_EXCLUDE_UNSECURE;
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;
@@ -10,6 +10,9 @@
#define TUNCONF_OVERRIDE_STRICTER 0x00000008
#define TUNCONF_OVERRIDE_DENY 0x0000000C
+#define TUNCONF_EXCLUDE_SECURE 0x00000010
+#define TUNCONF_EXCLUDE_UNSECURE 0x00000020
+
#define TUNCONF_FLAG_FILTER 0x0000ff00
#define TUNCONF_FILTER_PERPROC 0x00000100
@@ -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 ();
@@ -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 ();