You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
409 lines
12 KiB
409 lines
12 KiB
2 years ago
|
commit 3b856d093f5197637a5927c37d6c07dad8c86d45
|
||
|
Author: Florian Weimer <fweimer@redhat.com>
|
||
|
Date: Tue Feb 12 13:36:56 2019 +0100
|
||
|
|
||
|
elf: Ignore LD_AUDIT interfaces if la_version returns 0 [BZ #24122]
|
||
|
|
||
|
This change moves the audit module loading and early notification into
|
||
|
separate functions out of dl_main.
|
||
|
|
||
|
It restores the bug fix from commit
|
||
|
8e889c5da3c5981c5a46a93fec02de40131ac5a6 ("elf: Fix LD_AUDIT for
|
||
|
modules with invalid version (BZ#24122)") which was reverted in commit
|
||
|
83e6b59625f45db1eee93e5684091f740c52a083 ("[elf] Revert 8e889c5da3
|
||
|
(BZ#24122)").
|
||
|
|
||
|
The actual bug fix is the separate error message for the case when
|
||
|
la_version returns zero. The dynamic linker error message (which is
|
||
|
NULL in this case) is no longer used. Based on the intended use of
|
||
|
version zero (ignore this module due to explicit request), the message
|
||
|
is only printed if debugging is enabled.
|
||
|
|
||
|
diff --git a/elf/rtld.c b/elf/rtld.c
|
||
|
index 8bb5f548a0ff8eb4..375e0de8fa2e049e 100644
|
||
|
--- a/elf/rtld.c
|
||
|
+++ b/elf/rtld.c
|
||
|
@@ -864,6 +864,205 @@ handle_preload_list (const char *preloadlist, struct link_map *main_map,
|
||
|
return npreloads;
|
||
|
}
|
||
|
|
||
|
+/* Called if the audit DSO cannot be used: if it does not have the
|
||
|
+ appropriate interfaces, or it expects a more recent version library
|
||
|
+ version than what the dynamic linker provides. */
|
||
|
+static void
|
||
|
+unload_audit_module (struct link_map *map, int original_tls_idx)
|
||
|
+{
|
||
|
+#ifndef NDEBUG
|
||
|
+ Lmid_t ns = map->l_ns;
|
||
|
+#endif
|
||
|
+ _dl_close (map);
|
||
|
+
|
||
|
+ /* Make sure the namespace has been cleared entirely. */
|
||
|
+ assert (GL(dl_ns)[ns]._ns_loaded == NULL);
|
||
|
+ assert (GL(dl_ns)[ns]._ns_nloaded == 0);
|
||
|
+
|
||
|
+ GL(dl_tls_max_dtv_idx) = original_tls_idx;
|
||
|
+}
|
||
|
+
|
||
|
+/* Called to print an error message if loading of an audit module
|
||
|
+ failed. */
|
||
|
+static void
|
||
|
+report_audit_module_load_error (const char *name, const char *err_str,
|
||
|
+ bool malloced)
|
||
|
+{
|
||
|
+ _dl_error_printf ("\
|
||
|
+ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
|
||
|
+ name, err_str);
|
||
|
+ if (malloced)
|
||
|
+ free ((char *) err_str);
|
||
|
+}
|
||
|
+
|
||
|
+/* Load one audit module. */
|
||
|
+static void
|
||
|
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
|
||
|
+{
|
||
|
+ int original_tls_idx = GL(dl_tls_max_dtv_idx);
|
||
|
+
|
||
|
+ struct dlmopen_args dlmargs;
|
||
|
+ dlmargs.fname = name;
|
||
|
+ dlmargs.map = NULL;
|
||
|
+
|
||
|
+ const char *objname;
|
||
|
+ const char *err_str = NULL;
|
||
|
+ bool malloced;
|
||
|
+ _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit, &dlmargs);
|
||
|
+ if (__glibc_unlikely (err_str != NULL))
|
||
|
+ {
|
||
|
+ report_audit_module_load_error (name, err_str, malloced);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ struct lookup_args largs;
|
||
|
+ largs.name = "la_version";
|
||
|
+ largs.map = dlmargs.map;
|
||
|
+ _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
|
||
|
+ if (__glibc_likely (err_str != NULL))
|
||
|
+ {
|
||
|
+ unload_audit_module (dlmargs.map, original_tls_idx);
|
||
|
+ report_audit_module_load_error (name, err_str, malloced);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ unsigned int (*laversion) (unsigned int) = largs.result;
|
||
|
+
|
||
|
+ /* A null symbol indicates that something is very wrong with the
|
||
|
+ loaded object because defined symbols are supposed to have a
|
||
|
+ valid, non-null address. */
|
||
|
+ assert (laversion != NULL);
|
||
|
+
|
||
|
+ unsigned int lav = laversion (LAV_CURRENT);
|
||
|
+ if (lav == 0)
|
||
|
+ {
|
||
|
+ /* Only print an error message if debugging because this can
|
||
|
+ happen deliberately. */
|
||
|
+ if (GLRO(dl_debug_mask) & DL_DEBUG_FILES)
|
||
|
+ _dl_debug_printf ("\
|
||
|
+file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
|
||
|
+ dlmargs.map->l_name, dlmargs.map->l_ns);
|
||
|
+ unload_audit_module (dlmargs.map, original_tls_idx);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (lav > LAV_CURRENT)
|
||
|
+ {
|
||
|
+ _dl_debug_printf ("\
|
||
|
+ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
|
||
|
+ name, lav, LAV_CURRENT);
|
||
|
+ unload_audit_module (dlmargs.map, original_tls_idx);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ enum { naudit_ifaces = 8 };
|
||
|
+ union
|
||
|
+ {
|
||
|
+ struct audit_ifaces ifaces;
|
||
|
+ void (*fptr[naudit_ifaces]) (void);
|
||
|
+ } *newp = malloc (sizeof (*newp));
|
||
|
+ if (newp == NULL)
|
||
|
+ _dl_fatal_printf ("Out of memory while loading audit modules\n");
|
||
|
+
|
||
|
+ /* Names of the auditing interfaces. All in one
|
||
|
+ long string. */
|
||
|
+ static const char audit_iface_names[] =
|
||
|
+ "la_activity\0"
|
||
|
+ "la_objsearch\0"
|
||
|
+ "la_objopen\0"
|
||
|
+ "la_preinit\0"
|
||
|
+#if __ELF_NATIVE_CLASS == 32
|
||
|
+ "la_symbind32\0"
|
||
|
+#elif __ELF_NATIVE_CLASS == 64
|
||
|
+ "la_symbind64\0"
|
||
|
+#else
|
||
|
+# error "__ELF_NATIVE_CLASS must be defined"
|
||
|
+#endif
|
||
|
+#define STRING(s) __STRING (s)
|
||
|
+ "la_" STRING (ARCH_LA_PLTENTER) "\0"
|
||
|
+ "la_" STRING (ARCH_LA_PLTEXIT) "\0"
|
||
|
+ "la_objclose\0";
|
||
|
+ unsigned int cnt = 0;
|
||
|
+ const char *cp = audit_iface_names;
|
||
|
+ do
|
||
|
+ {
|
||
|
+ largs.name = cp;
|
||
|
+ _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
|
||
|
+
|
||
|
+ /* Store the pointer. */
|
||
|
+ if (err_str == NULL && largs.result != NULL)
|
||
|
+ {
|
||
|
+ newp->fptr[cnt] = largs.result;
|
||
|
+
|
||
|
+ /* The dynamic linker link map is statically allocated,
|
||
|
+ initialize the data now. */
|
||
|
+ GL(dl_rtld_map).l_audit[cnt].cookie = (intptr_t) &GL(dl_rtld_map);
|
||
|
+ }
|
||
|
+ else
|
||
|
+ newp->fptr[cnt] = NULL;
|
||
|
+ ++cnt;
|
||
|
+
|
||
|
+ cp = rawmemchr (cp, '\0') + 1;
|
||
|
+ }
|
||
|
+ while (*cp != '\0');
|
||
|
+ assert (cnt == naudit_ifaces);
|
||
|
+
|
||
|
+ /* Now append the new auditing interface to the list. */
|
||
|
+ newp->ifaces.next = NULL;
|
||
|
+ if (*last_audit == NULL)
|
||
|
+ *last_audit = GLRO(dl_audit) = &newp->ifaces;
|
||
|
+ else
|
||
|
+ *last_audit = (*last_audit)->next = &newp->ifaces;
|
||
|
+ ++GLRO(dl_naudit);
|
||
|
+
|
||
|
+ /* Mark the DSO as being used for auditing. */
|
||
|
+ dlmargs.map->l_auditing = 1;
|
||
|
+}
|
||
|
+
|
||
|
+/* Notify the the audit modules that the object MAP has already been
|
||
|
+ loaded. */
|
||
|
+static void
|
||
|
+notify_audit_modules_of_loaded_object (struct link_map *map)
|
||
|
+{
|
||
|
+ struct audit_ifaces *afct = GLRO(dl_audit);
|
||
|
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
|
||
|
+ {
|
||
|
+ if (afct->objopen != NULL)
|
||
|
+ {
|
||
|
+ map->l_audit[cnt].bindflags
|
||
|
+ = afct->objopen (map, LM_ID_BASE, &map->l_audit[cnt].cookie);
|
||
|
+ map->l_audit_any_plt |= map->l_audit[cnt].bindflags != 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ afct = afct->next;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/* Load all audit modules. */
|
||
|
+static void
|
||
|
+load_audit_modules (struct link_map *main_map)
|
||
|
+{
|
||
|
+ struct audit_ifaces *last_audit = NULL;
|
||
|
+ struct audit_list_iter al_iter;
|
||
|
+ audit_list_iter_init (&al_iter);
|
||
|
+
|
||
|
+ while (true)
|
||
|
+ {
|
||
|
+ const char *name = audit_list_iter_next (&al_iter);
|
||
|
+ if (name == NULL)
|
||
|
+ break;
|
||
|
+ load_audit_module (name, &last_audit);
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Notify audit modules of the initially loaded modules (the main
|
||
|
+ program and the dynamic linker itself). */
|
||
|
+ if (GLRO(dl_naudit) > 0)
|
||
|
+ {
|
||
|
+ notify_audit_modules_of_loaded_object (main_map);
|
||
|
+ notify_audit_modules_of_loaded_object (&GL(dl_rtld_map));
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
static void
|
||
|
dl_main (const ElfW(Phdr) *phdr,
|
||
|
ElfW(Word) phnum,
|
||
|
@@ -1402,10 +1601,6 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
|
||
|
if (__glibc_unlikely (audit_list != NULL)
|
||
|
|| __glibc_unlikely (audit_list_string != NULL))
|
||
|
{
|
||
|
- struct audit_ifaces *last_audit = NULL;
|
||
|
- struct audit_list_iter al_iter;
|
||
|
- audit_list_iter_init (&al_iter);
|
||
|
-
|
||
|
/* Since we start using the auditing DSOs right away we need to
|
||
|
initialize the data structures now. */
|
||
|
tcbp = init_tls ();
|
||
|
@@ -1417,164 +1612,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
|
||
|
security_init ();
|
||
|
need_security_init = false;
|
||
|
|
||
|
- while (true)
|
||
|
- {
|
||
|
- const char *name = audit_list_iter_next (&al_iter);
|
||
|
- if (name == NULL)
|
||
|
- break;
|
||
|
-
|
||
|
- int tls_idx = GL(dl_tls_max_dtv_idx);
|
||
|
-
|
||
|
- /* Now it is time to determine the layout of the static TLS
|
||
|
- block and allocate it for the initial thread. Note that we
|
||
|
- always allocate the static block, we never defer it even if
|
||
|
- no DF_STATIC_TLS bit is set. The reason is that we know
|
||
|
- glibc will use the static model. */
|
||
|
- struct dlmopen_args dlmargs;
|
||
|
- dlmargs.fname = name;
|
||
|
- dlmargs.map = NULL;
|
||
|
-
|
||
|
- const char *objname;
|
||
|
- const char *err_str = NULL;
|
||
|
- bool malloced;
|
||
|
- (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit,
|
||
|
- &dlmargs);
|
||
|
- if (__glibc_unlikely (err_str != NULL))
|
||
|
- {
|
||
|
- not_loaded:
|
||
|
- _dl_error_printf ("\
|
||
|
-ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
|
||
|
- name, err_str);
|
||
|
- if (malloced)
|
||
|
- free ((char *) err_str);
|
||
|
- }
|
||
|
- else
|
||
|
- {
|
||
|
- struct lookup_args largs;
|
||
|
- largs.name = "la_version";
|
||
|
- largs.map = dlmargs.map;
|
||
|
-
|
||
|
- /* Check whether the interface version matches. */
|
||
|
- (void) _dl_catch_error (&objname, &err_str, &malloced,
|
||
|
- lookup_doit, &largs);
|
||
|
-
|
||
|
- unsigned int (*laversion) (unsigned int);
|
||
|
- unsigned int lav;
|
||
|
- if (err_str == NULL
|
||
|
- && (laversion = largs.result) != NULL
|
||
|
- && (lav = laversion (LAV_CURRENT)) > 0
|
||
|
- && lav <= LAV_CURRENT)
|
||
|
- {
|
||
|
- /* Allocate structure for the callback function pointers.
|
||
|
- This call can never fail. */
|
||
|
- union
|
||
|
- {
|
||
|
- struct audit_ifaces ifaces;
|
||
|
-#define naudit_ifaces 8
|
||
|
- void (*fptr[naudit_ifaces]) (void);
|
||
|
- } *newp = malloc (sizeof (*newp));
|
||
|
-
|
||
|
- /* Names of the auditing interfaces. All in one
|
||
|
- long string. */
|
||
|
- static const char audit_iface_names[] =
|
||
|
- "la_activity\0"
|
||
|
- "la_objsearch\0"
|
||
|
- "la_objopen\0"
|
||
|
- "la_preinit\0"
|
||
|
-#if __ELF_NATIVE_CLASS == 32
|
||
|
- "la_symbind32\0"
|
||
|
-#elif __ELF_NATIVE_CLASS == 64
|
||
|
- "la_symbind64\0"
|
||
|
-#else
|
||
|
-# error "__ELF_NATIVE_CLASS must be defined"
|
||
|
-#endif
|
||
|
-#define STRING(s) __STRING (s)
|
||
|
- "la_" STRING (ARCH_LA_PLTENTER) "\0"
|
||
|
- "la_" STRING (ARCH_LA_PLTEXIT) "\0"
|
||
|
- "la_objclose\0";
|
||
|
- unsigned int cnt = 0;
|
||
|
- const char *cp = audit_iface_names;
|
||
|
- do
|
||
|
- {
|
||
|
- largs.name = cp;
|
||
|
- (void) _dl_catch_error (&objname, &err_str, &malloced,
|
||
|
- lookup_doit, &largs);
|
||
|
-
|
||
|
- /* Store the pointer. */
|
||
|
- if (err_str == NULL && largs.result != NULL)
|
||
|
- {
|
||
|
- newp->fptr[cnt] = largs.result;
|
||
|
-
|
||
|
- /* The dynamic linker link map is statically
|
||
|
- allocated, initialize the data now. */
|
||
|
- GL(dl_rtld_map).l_audit[cnt].cookie
|
||
|
- = (intptr_t) &GL(dl_rtld_map);
|
||
|
- }
|
||
|
- else
|
||
|
- newp->fptr[cnt] = NULL;
|
||
|
- ++cnt;
|
||
|
-
|
||
|
- cp = (char *) rawmemchr (cp, '\0') + 1;
|
||
|
- }
|
||
|
- while (*cp != '\0');
|
||
|
- assert (cnt == naudit_ifaces);
|
||
|
-
|
||
|
- /* Now append the new auditing interface to the list. */
|
||
|
- newp->ifaces.next = NULL;
|
||
|
- if (last_audit == NULL)
|
||
|
- last_audit = GLRO(dl_audit) = &newp->ifaces;
|
||
|
- else
|
||
|
- last_audit = last_audit->next = &newp->ifaces;
|
||
|
- ++GLRO(dl_naudit);
|
||
|
-
|
||
|
- /* Mark the DSO as being used for auditing. */
|
||
|
- dlmargs.map->l_auditing = 1;
|
||
|
- }
|
||
|
- else
|
||
|
- {
|
||
|
- /* We cannot use the DSO, it does not have the
|
||
|
- appropriate interfaces or it expects something
|
||
|
- more recent. */
|
||
|
-#ifndef NDEBUG
|
||
|
- Lmid_t ns = dlmargs.map->l_ns;
|
||
|
-#endif
|
||
|
- _dl_close (dlmargs.map);
|
||
|
-
|
||
|
- /* Make sure the namespace has been cleared entirely. */
|
||
|
- assert (GL(dl_ns)[ns]._ns_loaded == NULL);
|
||
|
- assert (GL(dl_ns)[ns]._ns_nloaded == 0);
|
||
|
-
|
||
|
- GL(dl_tls_max_dtv_idx) = tls_idx;
|
||
|
- goto not_loaded;
|
||
|
- }
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- /* If we have any auditing modules, announce that we already
|
||
|
- have two objects loaded. */
|
||
|
- if (__glibc_unlikely (GLRO(dl_naudit) > 0))
|
||
|
- {
|
||
|
- struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) };
|
||
|
-
|
||
|
- for (unsigned int outer = 0; outer < 2; ++outer)
|
||
|
- {
|
||
|
- struct audit_ifaces *afct = GLRO(dl_audit);
|
||
|
- for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
|
||
|
- {
|
||
|
- if (afct->objopen != NULL)
|
||
|
- {
|
||
|
- ls[outer]->l_audit[cnt].bindflags
|
||
|
- = afct->objopen (ls[outer], LM_ID_BASE,
|
||
|
- &ls[outer]->l_audit[cnt].cookie);
|
||
|
-
|
||
|
- ls[outer]->l_audit_any_plt
|
||
|
- |= ls[outer]->l_audit[cnt].bindflags != 0;
|
||
|
- }
|
||
|
-
|
||
|
- afct = afct->next;
|
||
|
- }
|
||
|
- }
|
||
|
- }
|
||
|
+ load_audit_modules (main_map);
|
||
|
}
|
||
|
|
||
|
/* Keep track of the currently loaded modules to count how many
|