From: Carlos O'Donell <carlos@redhat.com>
To: Adhemerval Zanella <adhemerval.zanella@linaro.org>,
libc-alpha@sourceware.org
Subject: Re: [PATCH] elf: Fix LD_AUDIT for modules with invalid version (BZ#24122)
Date: Wed, 23 Jan 2019 14:58:00 -0000 [thread overview]
Message-ID: <ba5e7dd2-b571-5589-91fb-d97bead14b5e@redhat.com> (raw)
In-Reply-To: <20190123140740.27433-1-adhemerval.zanella@linaro.org>
On 1/23/19 9:07 AM, Adhemerval Zanella wrote:
Thanks for fixing this!
I'd like to see v2 please
- Only output message when LD_DEBUG=all, since la_version returning 0
or > LAV_CURRENT is not an error, but simply an API mandated situation
where we ignore the module. I don't see that as an error.
The Solaris docs say:
"If the audit library return is zero, or a version that is greater
than the rtld-audit interface the runtime linker supports, the audit
library is discarded."
Just discarded. It is up to the auditor to decide how it exposes to
the user that it is running and operating as expected. We can provide
feedback via LD_DEBUG=all.
- Optional: use -Wl,z,lazy for the test, or remove the comment about
the PLT call.
- Suggested comment in tst-audit13mod1.c
> The error handling patch for invalid audit modules version access
> invalid memory:
>
> elf/rtld.c:
>
> 1454 unsigned int (*laversion) (unsigned int);
> 1455 unsigned int lav;
> 1456 if (err_str == NULL
> 1457 && (laversion = largs.result) != NULL
> 1458 && (lav = laversion (LAV_CURRENT)) > 0
> 1459 && lav <= LAV_CURRENT)
> 1460 {
> [...]
> 1526 else
> 1527 {
> 1528 /* We cannot use the DSO, it does not have the
> 1529 appropriate interfaces or it expects something
> 1530 more recent. */
> 1531 #ifndef NDEBUG
> 1532 Lmid_t ns = dlmargs.map->l_ns;
> 1533 #endif
> 1534 _dl_close (dlmargs.map);
> 1535
> 1536 /* Make sure the namespace has been cleared entirely. */
> 1537 assert (GL(dl_ns)[ns]._ns_loaded == NULL);
> 1538 assert (GL(dl_ns)[ns]._ns_nloaded == 0);
> 1539
> 1540 GL(dl_tls_max_dtv_idx) = tls_idx;
> 1541 goto not_loaded;
> 1542 }
>
> 1431 const char *err_str = NULL;
> 1432 bool malloced;
> 1433 (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit,
> 1434 &dlmargs);
> 1435 if (__glibc_unlikely (err_str != NULL))
> 1436 {
> 1437 not_loaded:
> 1438 _dl_error_printf ("\
> 1439 ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
> 1440 name, err_str);
> 1441 if (malloced)
> 1442 free ((char *) err_str);
> 1443 }
>
> On failure the err_str will be NULL and _dl_debug_vdprintf does not handle
> it properly:
>
> elf/dl-misc.c:
> 200 case 's':
> 201 /* Get the string argument. */
> 202 iov[niov].iov_base = va_arg (arg, char *);
> 203 iov[niov].iov_len = strlen (iov[niov].iov_base);
> 204 if (prec != -1)
> 205 iov[niov].iov_len = MIN ((size_t) prec, iov[niov].iov_len);
> 206 ++niov;
> 207 break;
>
> This patch fixes the issues and improves the error message.
>
> Checked on x86_64-linux-gnu and i686-linux-gnu
>
> [BZ #24122]
> * elf/Makefile (tests): Add tst-audit13.
> (modules-names): Add tst-audit13mod1.
> (tst-audit13.out, tst-audit13-ENV): New rule.
> * elf/rtld.c (dl_main): Handle invalid audit module version.
> * elf/tst-audit13.c: New file.
> * elf/tst-audit13mod1.c: Likewise.
> ---
> ChangeLog | 10 +++++
> elf/Makefile | 8 +++-
> elf/rtld.c | 19 ++++++---
> elf/tst-audit13.c | 28 +++++++++++++
> elf/tst-audit13mod1.c | 91 +++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 149 insertions(+), 7 deletions(-)
> create mode 100644 elf/tst-audit13.c
> create mode 100644 elf/tst-audit13mod1.c
>
> diff --git a/elf/Makefile b/elf/Makefile
> index 9cf5cd8dfd..f71ed7cfff 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -187,7 +187,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
> tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
> tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
> tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
> - tst-unwind-ctor tst-unwind-main
> + tst-unwind-ctor tst-unwind-main tst-audit13
> # reldep9
> tests-internal += loadtest unload unload2 circleload1 \
> neededtest neededtest2 neededtest3 neededtest4 \
> @@ -275,7 +275,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
> tst-latepthreadmod $(tst-tls-many-dynamic-modules) \
> tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
> tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
> - tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib
> + tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \
> + tst-audit13mod1
> # Most modules build with _ISOMAC defined, but those filtered out
> # depend on internal headers.
> modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
> @@ -1382,6 +1383,9 @@ tst-audit12-ENV = LD_AUDIT=$(objpfx)tst-auditmod12.so
> $(objpfx)tst-audit12mod1.so: $(objpfx)tst-audit12mod2.so
> LDFLAGS-tst-audit12mod2.so = -Wl,--version-script=tst-audit12mod2.map
>
> +$(objpfx)tst-audit13.out: $(objpfx)tst-audit13mod1.so
> +tst-audit13-ENV = LD_AUDIT=$(objpfx)tst-audit13mod1.so
OK.
> +
> # Override -z defs, so that we can reference an undefined symbol.
> # Force lazy binding for the same reason.
> LDFLAGS-tst-latepthreadmod.so = \
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 5d97f41b7b..ad62c58e17 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -1453,10 +1453,12 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
>
> unsigned int (*laversion) (unsigned int);
> unsigned int lav;
> - if (err_str == NULL
> - && (laversion = largs.result) != NULL
> - && (lav = laversion (LAV_CURRENT)) > 0
> - && lav <= LAV_CURRENT)
> + if (err_str != NULL)
> + goto not_loaded;
OK. This means we had a dlopen failure of the module, which is something we
*should* raise an error about.
> +
> + if ((laversion = largs.result) != NULL
> + && (lav = laversion (LAV_CURRENT)) > 0
> + && lav <= LAV_CURRENT)
OK, this means the DSO is loaded, and we run la_version symbol and check
for the validity of the results. If lav == 0 or lav > LAV_CURRENT then we
ignore the module.
> {
> /* Allocate structure for the callback function pointers.
> This call can never fail. */
> @@ -1538,7 +1540,14 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
> assert (GL(dl_ns)[ns]._ns_nloaded == 0);
>
> GL(dl_tls_max_dtv_idx) = tls_idx;
> - goto not_loaded;
OK, this is the "ignore the module case."
> + _dl_error_printf ("\
> +ERROR: ld.so: object '%s' cannot be loaded as audit interface: ", name);
> + if (laversion == NULL)
> + _dl_error_printf ("la_version function not found.\n");
> + else
> + _dl_error_printf (
> +"invalid version '%u' (expected minimum of '%u'); ignored.\n",
> + lav, LAV_CURRENT);
If this message is output unconditionally, then I think this is wrong.
We must only output a message in the event of LD_DEBUG=all.
It is not "wrong" to return 0 or larger than the audit version, it is
simply part of the API, and is not an error that should generate a
message the user would see. The module is silently dropped. I think this
is important for modules which dynamically disable themselves. We don't
want those module to start printing errors at load time because of them.
Consider a module that only audits when the sysadmin sets a system
parameter, and otherwise disables itself by returning 0. This should not
be an error if LD_AUDIT remains set (as it should be to keep the module
checking for the admin parameter).
During LD_DEBUG=all you definitely want to see these audit issues
as a developer of the application, but not as a user running the
application.
> }
> }
> }
> diff --git a/elf/tst-audit13.c b/elf/tst-audit13.c
> new file mode 100644
> index 0000000000..6f587baf58
> --- /dev/null
> +++ b/elf/tst-audit13.c
> @@ -0,0 +1,28 @@
> +/* Check for invalid audit version (BZ#24122).
> + Copyright (C) 2019 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
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +
> +static int
> +do_test (void)
> +{
> + puts ("plt call");
> + return 0;
OK. Note that there might not be a PLT call here if the distro was
defaulting to -Wl,-z,now. I don't think it matters in this case though.
If you really want that PLT call you must use cflags -Wl,-z,lazy when
you build the test.
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-audit13mod1.c b/elf/tst-audit13mod1.c
> new file mode 100644
> index 0000000000..96f1adef7a
> --- /dev/null
> +++ b/elf/tst-audit13mod1.c
> @@ -0,0 +1,91 @@
> +/* Check for invalid audit version (BZ#24122).
> + Copyright (C) 2019 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
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <link.h>
> +#include <stdlib.h>
> +
> +unsigned int
> +la_version (unsigned int version)
> +{
> + /* Invalid version, object should be discarded by the loader. */
Suggest:
/* The audit specification says that a version of 0 or a version
greater than any version supported by the dynamic loader shall
cause the module to be ignored. */
Basically "return 0;" is the way to dynamically disable the auditor.
> + return 0;
> +}
> +
> +void
> +la_activity (uintptr_t *cookie, unsigned int flag)
> +{
> + exit (EXIT_FAILURE);
> +}
> +
> +char *
> +la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
> +{
> + exit (EXIT_FAILURE);
> +}
> +
> +unsigned int
> +la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t * cookie)
> +{
> + exit (EXIT_FAILURE);
> +}
> +
> +void
> +la_preinit (uintptr_t * cookie)
> +{
> + exit (EXIT_FAILURE);
> +}
OK. Perfect. I like the exit (EXIT_FAILURE); :-)
> +
> +uintptr_t
> +#if __ELF_NATIVE_CLASS == 32
> +la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
> + uintptr_t *defcook, unsigned int *flags, const char *symname)
> +#else
> +la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
> + uintptr_t *defcook, unsigned int *flags, const char *symname)
> +#endif
> +{
> + exit (EXIT_FAILURE);
> +}
> +
> +unsigned int
> +la_objclose (uintptr_t * cookie)
> +{
> + exit (EXIT_FAILURE);
> +}
> +
> +#include <tst-audit.h>
> +#if (!defined (pltenter) || !defined (pltexit) || !defined (La_regs) \
> + || !defined (La_retval) || !defined (int_retval))
> +# error "architecture specific code needed in sysdeps/CPU/tst-audit.h"
> +#endif
> +
> +ElfW(Addr)
> +pltenter (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
> + uintptr_t *defcook, La_regs *regs, unsigned int *flags,
> + const char *symname, long int *framesizep)
> +{
> + exit (EXIT_FAILURE);
> +}
> +
> +unsigned int
> +pltexit (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
> + uintptr_t *defcook, const La_regs *inregs, La_retval *outregs,
> + const char *symname)
> +{
> + exit (EXIT_FAILURE);
> +}
>
OK.
--
Cheers,
Carlos.
next prev parent reply other threads:[~2019-01-23 14:58 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-23 14:07 Adhemerval Zanella
2019-01-23 14:58 ` Carlos O'Donell [this message]
2019-01-23 17:45 ` Adhemerval Zanella
2019-01-23 18:02 ` Carlos O'Donell
2019-01-23 19:01 ` Adhemerval Zanella
2019-01-23 19:59 ` Adhemerval Zanella
2019-01-23 20:44 ` Carlos O'Donell
2019-01-24 6:45 ` Siddhesh Poyarekar
2019-01-24 12:33 ` Florian Weimer
2019-01-24 12:50 ` Adhemerval Zanella
2019-01-24 13:20 ` Florian Weimer
2019-01-24 13:46 ` Adhemerval Zanella
2019-01-24 14:16 ` Florian Weimer
2019-01-24 14:32 ` Carlos O'Donell
2019-01-24 15:16 ` Adhemerval Zanella
2019-01-25 1:07 ` Carlos O'Donell
2019-01-25 1:38 ` Siddhesh Poyarekar
2019-01-25 10:28 ` Adhemerval Zanella
2019-01-24 15:02 ` Adhemerval Zanella
2019-01-25 20:19 ` Florian Weimer
2019-01-25 20:21 ` Florian Weimer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ba5e7dd2-b571-5589-91fb-d97bead14b5e@redhat.com \
--to=carlos@redhat.com \
--cc=adhemerval.zanella@linaro.org \
--cc=libc-alpha@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).