elf: Issue audit la_objopen for vDSO

The vDSO is is listed in the link_map chain, but is never the subject of
an la_objopen call.  A new internal flag __RTLD_VDSO is added that
acts as __RTLD_OPENEXEC to allocate the required 'struct auditstate'
extra space for the 'struct link_map'.

The return value from the callback is currently ignored, since there
is no PLT call involved by glibc when using the vDSO, neither the vDSO
are exported directly.

Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit f0e23d34a7)

Resolved conflicts:
	elf/Makefile
This commit is contained in:
Adhemerval Zanella 2021-07-19 18:42:26 -03:00 committed by Carlos O'Donell
parent 02c6a3d353
commit d1b9bee29a
7 changed files with 199 additions and 8 deletions

View File

@ -355,6 +355,7 @@ tests += \
tst-audit17 \ tst-audit17 \
tst-audit18 \ tst-audit18 \
tst-audit19b \ tst-audit19b \
tst-audit22 \
tst-auditmany \ tst-auditmany \
tst-auxobj \ tst-auxobj \
tst-auxobj-dlopen \ tst-auxobj-dlopen \
@ -633,6 +634,7 @@ modules-names = \
tst-auditmod18 \ tst-auditmod18 \
tst-auditmod19a \ tst-auditmod19a \
tst-auditmod19b \ tst-auditmod19b \
tst-auditmod22 \
tst-auxvalmod \ tst-auxvalmod \
tst-big-note-lib \ tst-big-note-lib \
tst-deep1mod1 \ tst-deep1mod1 \
@ -1991,6 +1993,9 @@ $(objpfx)tst-audit19b.out: $(objpfx)tst-auditmod19b.so
$(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so $(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so
tst-audit19b-ARGS = -- $(host-test-program-cmd) tst-audit19b-ARGS = -- $(host-test-program-cmd)
$(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
tst-audit22-ARGS = -- $(host-test-program-cmd)
# tst-sonamemove links against an older implementation of the library. # tst-sonamemove links against an older implementation of the library.
LDFLAGS-tst-sonamemove-linkmod1.so = \ LDFLAGS-tst-sonamemove-linkmod1.so = \
-Wl,--version-script=tst-sonamemove-linkmod1.map \ -Wl,--version-script=tst-sonamemove-linkmod1.map \

View File

@ -59,7 +59,9 @@ _dl_new_object (char *realname, const char *libname, int type,
{ {
#ifdef SHARED #ifdef SHARED
unsigned int naudit; unsigned int naudit;
if (__glibc_unlikely ((mode & __RTLD_OPENEXEC) != 0)) if (__glibc_unlikely ((mode & (__RTLD_OPENEXEC | __RTLD_VDSO)) != 0))
{
if (mode & __RTLD_OPENEXEC)
{ {
assert (type == lt_executable); assert (type == lt_executable);
assert (nsid == LM_ID_BASE); assert (nsid == LM_ID_BASE);
@ -67,8 +69,9 @@ _dl_new_object (char *realname, const char *libname, int type,
/* Ignore the specified libname for the main executable. It is /* Ignore the specified libname for the main executable. It is
only known with an explicit loader invocation. */ only known with an explicit loader invocation. */
libname = ""; libname = "";
}
/* We create the map for the executable before we know whether /* We create the map for the executable and vDSO before we know whether
we have auditing libraries and if yes, how many. Assume the we have auditing libraries and if yes, how many. Assume the
worst. */ worst. */
naudit = DL_NNS; naudit = DL_NNS;

View File

@ -1885,6 +1885,12 @@ dl_main (const ElfW(Phdr) *phdr,
assert (i == npreloads); assert (i == npreloads);
} }
#ifdef NEED_DL_SYSINFO_DSO
/* Now that the audit modules are opened, call la_objopen for the vDSO. */
if (GLRO(dl_sysinfo_map) != NULL)
_dl_audit_objopen (GLRO(dl_sysinfo_map), LM_ID_BASE);
#endif
/* Load all the libraries specified by DT_NEEDED entries. If LD_PRELOAD /* Load all the libraries specified by DT_NEEDED entries. If LD_PRELOAD
specified some libraries to load, these are inserted before the actual specified some libraries to load, these are inserted before the actual
dependencies in the executable's searchlist for symbol resolution. */ dependencies in the executable's searchlist for symbol resolution. */

View File

@ -30,7 +30,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
We just want our data structures to describe it as if we had just We just want our data structures to describe it as if we had just
mapped and relocated it normally. */ mapped and relocated it normally. */
struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL, struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL,
0, LM_ID_BASE); __RTLD_VDSO, LM_ID_BASE);
if (__glibc_likely (l != NULL)) if (__glibc_likely (l != NULL))
{ {
l->l_phdr = ((const void *) GLRO(dl_sysinfo_dso) l->l_phdr = ((const void *) GLRO(dl_sysinfo_dso)

124
elf/tst-audit22.c Normal file
View File

@ -0,0 +1,124 @@
/* Check DTAUDIT and vDSO interaction.
Copyright (C) 2021 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 <errno.h>
#include <getopt.h>
#include <limits.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/xstdio.h>
#include <support/support.h>
#include <sys/auxv.h>
static int restart;
#define CMDLINE_OPTIONS \
{ "restart", no_argument, &restart, 1 },
static uintptr_t vdso_addr;
static int
handle_restart (void)
{
fprintf (stderr, "vdso: %p\n", (void*) vdso_addr);
return 0;
}
static uintptr_t
parse_address (const char *str)
{
void *r;
TEST_COMPARE (sscanf (str, "%p\n", &r), 1);
return (uintptr_t) r;
}
static inline bool
startswith (const char *str, const char *pre)
{
size_t lenpre = strlen (pre);
size_t lenstr = strlen (str);
return lenstr >= lenpre && memcmp (pre, str, lenpre) == 0;
}
static int
do_test (int argc, char *argv[])
{
vdso_addr = getauxval (AT_SYSINFO_EHDR);
if (vdso_addr == 0)
FAIL_UNSUPPORTED ("getauxval (AT_SYSINFO_EHDR) returned 0");
/* We must have either:
- One our fource parameters left if called initially:
+ path to ld.so optional
+ "--library-path" optional
+ the library path optional
+ the application name */
if (restart)
return handle_restart ();
char *spargv[9];
int i = 0;
for (; i < argc - 1; i++)
spargv[i] = argv[i + 1];
spargv[i++] = (char *) "--direct";
spargv[i++] = (char *) "--restart";
spargv[i] = NULL;
setenv ("LD_AUDIT", "tst-auditmod22.so", 0);
struct support_capture_subprocess result
= support_capture_subprogram (spargv[0], spargv);
support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr);
/* The respawned process should always print the vDSO address (otherwise it
will fails as unsupported). However, on some architectures the audit
module might see the vDSO with l_addr being 0, meaning a fixed mapping
(linux-gate.so). In this case we don't check its value against
AT_SYSINFO_EHDR one. */
uintptr_t vdso_process = 0;
bool vdso_audit_found = false;
uintptr_t vdso_audit = 0;
FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
TEST_VERIFY (out != NULL);
char *buffer = NULL;
size_t buffer_length = 0;
while (xgetline (&buffer, &buffer_length, out))
{
if (startswith (buffer, "vdso: "))
vdso_process = parse_address (buffer + strlen ("vdso: "));
else if (startswith (buffer, "vdso found: "))
{
vdso_audit = parse_address (buffer + strlen ("vdso found: "));
vdso_audit_found = true;
}
}
TEST_COMPARE (vdso_audit_found, true);
if (vdso_audit != 0)
TEST_COMPARE (vdso_process, vdso_audit);
free (buffer);
xfclose (out);
return 0;
}
#define TEST_FUNCTION_ARGV do_test
#include <support/test-driver.c>

51
elf/tst-auditmod22.c Normal file
View File

@ -0,0 +1,51 @@
/* Check DTAUDIT and vDSO interaction.
Copyright (C) 2021 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 <link.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <sys/auxv.h>
static inline bool
startswith (const char *str, const char *pre)
{
size_t lenpre = strlen (pre);
size_t lenstr = strlen (str);
return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
}
unsigned int
la_version (unsigned int version)
{
return LAV_CURRENT;
}
unsigned int
la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
{
/* The linux-gate.so is placed at a fixed address, thus l_addr being 0,
and it might be the value reported as the AT_SYSINFO_EHDR. */
if (map->l_addr == 0 && startswith (map->l_name, "linux-gate.so"))
fprintf (stderr, "vdso found: %p\n", NULL);
else if (map->l_addr == getauxval (AT_SYSINFO_EHDR))
fprintf (stderr, "vdso found: %p\n", (void*) map->l_addr);
return 0;
}

View File

@ -12,6 +12,8 @@
#define __RTLD_AUDIT 0x08000000 #define __RTLD_AUDIT 0x08000000
#define __RTLD_SECURE 0x04000000 /* Apply additional security checks. */ #define __RTLD_SECURE 0x04000000 /* Apply additional security checks. */
#define __RTLD_NOIFUNC 0x02000000 /* Suppress calling ifunc functions. */ #define __RTLD_NOIFUNC 0x02000000 /* Suppress calling ifunc functions. */
#define __RTLD_VDSO 0x01000000 /* Tell _dl_new_object the object is
system-loaded. */
#define __LM_ID_CALLER -2 #define __LM_ID_CALLER -2