[RFC][PATCH v6 14/20] When loading DSOs into alternate namespaces check for DT_GNU_UNIQUE
Vivek Das Mohapatra
vivek@collabora.com
Tue Dec 15 18:44:54 GMT 2020
If a DSO has not already been loaded and the target is not the main
namespace then we must check to see if it's been DT_GNU_UNIQUE tagged
and load it into the main namespace instead.
dl_open_worker has alread been modified to notice the discrepancy
between the request and the result in such cases, and will set up
a proxy in the target namespace.
---
elf/dl-load.c | 96 +++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 82 insertions(+), 14 deletions(-)
diff --git a/elf/dl-load.c b/elf/dl-load.c
index a31f3771de..68587831fd 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -837,6 +837,62 @@ _dl_init_paths (const char *llp, const char *source,
__rtld_env_path_list.dirs = (void *) -1;
}
+static ElfW(Word)
+_has_gnu_unique (int fd, const ElfW(Ehdr) *header, const ElfW(Phdr) *phdr)
+{
+ int unique = 0;
+ const ElfW(Phdr) *ph;
+ ElfW(Dyn) entry = {};
+ off_t reset;
+ off_t pos;
+ off_t end;
+
+ reset = __lseek (fd, 0, SEEK_CUR);
+
+ for (ph = phdr; ph < &phdr[header->e_phnum]; ++ph)
+ {
+ switch (ph->p_type)
+ {
+ case PT_DYNAMIC:
+ pos = __lseek (fd, ph->p_offset, SEEK_SET);
+ end = pos + ph->p_filesz;
+
+ while (pos < end)
+ {
+ ssize_t rb = 0;
+ do
+ {
+ ssize_t rretl = __read_nocancel (fd, &entry + rb,
+ sizeof (ElfW(Dyn)) - rb);
+ if (rretl <= 0)
+ goto cleanup;
+
+ rb += rretl;
+ }
+ while (__glibc_unlikely (rb < sizeof (ElfW(Dyn))));
+
+ switch (entry.d_tag)
+ {
+ case DT_GNU_FLAGS_1:
+ unique = entry.d_un.d_val & DF_GNU_1_UNIQUE;
+ case DT_NULL:
+ goto cleanup;
+ break;
+ default:
+ break;
+ }
+ pos += rb;
+ }
+ break;
+ }
+ }
+
+ cleanup:
+ /* Put the file descriptor offset back where it was when we were called. */
+ __lseek (fd, reset, SEEK_SET);
+
+ return unique;
+}
/* Process PT_GNU_PROPERTY program header PH in module L after
PT_LOAD segments are mapped. Only one NT_GNU_PROPERTY_TYPE_0
@@ -1098,6 +1154,32 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
else
assert (r->r_state == RT_ADD);
+ /* Load the ELF header using preallocated struct space if it's big enough. */
+ maplength = header->e_phnum * sizeof (ElfW(Phdr));
+ if (header->e_phoff + maplength <= (size_t) fbp->len)
+ phdr = (void *) (fbp->buf + header->e_phoff);
+ else
+ {
+ phdr = alloca (maplength);
+ if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
+ header->e_phoff) != maplength)
+ {
+ errstring = N_("cannot read file data");
+ goto lose_errno;
+ }
+ }
+
+ /* We need to check for DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE before we start
+ initialising any namespace dependent metatada. */
+ if (nsid != LM_ID_BASE)
+ {
+ /* Target DSO is flagged as unique: Make sure it gets loaded into
+ the base namespace. It is up to our caller to generate a proxy in
+ the target nsid. */
+ if (_has_gnu_unique (fd, header, phdr))
+ nsid = LM_ID_BASE;
+ }
+
/* Enter the new object in the list of loaded objects. */
l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
if (__glibc_unlikely (l == NULL))
@@ -1115,20 +1197,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
type = header->e_type;
l->l_phnum = header->e_phnum;
- maplength = header->e_phnum * sizeof (ElfW(Phdr));
- if (header->e_phoff + maplength <= (size_t) fbp->len)
- phdr = (void *) (fbp->buf + header->e_phoff);
- else
- {
- phdr = alloca (maplength);
- if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
- header->e_phoff) != maplength)
- {
- errstring = N_("cannot read file data");
- goto lose_errno;
- }
- }
-
/* On most platforms presume that PT_GNU_STACK is absent and the stack is
* executable. Other platforms default to a nonexecutable stack and don't
* need PT_GNU_STACK to do so. */
--
2.20.1
More information about the Libc-alpha
mailing list