lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <965725522edc00c60b7e72c66970eb2844d0911b.1402769596.git.luto@amacapital.net>
Date:	Sat, 14 Jun 2014 11:16:44 -0700
From:	Andy Lutomirski <luto@...capital.net>
To:	Russ Cox <rsc@...ang.org>, linux-api@...r.kernel.org,
	Ian Taylor <iant@...ang.org>, linux-kernel@...r.kernel.org
Cc:	X86 ML <x86@...nel.org>, dalias@...c.org,
	Andy Lutomirski <luto@...capital.net>
Subject: [RFC 2/2] [NOT READY] x86/vdso: Add __vdso_findsym

The intent is to add a painless way for userspace libraries to find
vdso symbols.  This will work by adding a vdso function
__vdso_findsym and a new auxvec entry AT_VDSO_FINDSYM that points at
it.

This isn't done yet.  __vdso_findsym works, but it's only available
for the 64-bit vdso, and AT_VDSO_FINDSYM isn't implemented.

Getting this to work for x32 will require hackery or an actual x32
toolchain to build it.

Signed-off-by: Andy Lutomirski <luto@...capital.net>
---
 arch/x86/vdso/Makefile          |   3 +
 arch/x86/vdso/vdso-findsym.c    | 136 ++++++++++++++++++++++++++++++++++++++++
 arch/x86/vdso/vdso-layout.lds.S |  11 ++--
 arch/x86/vdso/vdso.lds.S        |   1 +
 arch/x86/vdso/vdso2c.h          |   2 +-
 5 files changed, 147 insertions(+), 6 deletions(-)
 create mode 100644 arch/x86/vdso/vdso-findsym.c

diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile
index ba6fc27..62e6938 100644
--- a/arch/x86/vdso/Makefile
+++ b/arch/x86/vdso/Makefile
@@ -16,7 +16,9 @@ vdso-install-$(VDSO32-y)	+= $(vdso32-images)
 
 # files to link into the vdso
 vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o
+vobjs-y += vdso-findsym.o
 vobjs-nox32 := vdso-fakesections.o
+vobjs-nox32 += vdso-findsym.o
 
 # files to link into kernel
 obj-y				+= vma.o
@@ -83,6 +85,7 @@ CFLAGS_REMOVE_vdso-note.o = -pg
 CFLAGS_REMOVE_vclock_gettime.o = -pg
 CFLAGS_REMOVE_vgetcpu.o = -pg
 CFLAGS_REMOVE_vvar.o = -pg
+CFLAGS_REMOVE_vdso-findsym.o = -pg
 
 #
 # X32 processes use x32 vDSO to access 64bit kernel data.
diff --git a/arch/x86/vdso/vdso-findsym.c b/arch/x86/vdso/vdso-findsym.c
new file mode 100644
index 0000000..83b0c0a
--- /dev/null
+++ b/arch/x86/vdso/vdso-findsym.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 Andy Lutomirski
+ * Subject to the GNU Public License, v.2
+ *
+ * AT_VDSO_FINDSYM implementation
+ *
+ * AT_VDSO_FINDSYM is a mechanism that can be used by userspace runtimes
+ * that don't want to implement a full ELF parser.  The ELF parser in
+ * here cheats heavily.  It relies on the linker to do part of the work,
+ * and it makes questionable assumptions about the layout of some of the
+ * dynamic data structures.  Those questionable assumptions are verified
+ * by vdso2c.
+ */
+
+/* Disable profiling for userspace code: */
+#define DISABLE_BRANCH_PROFILING
+
+#pragma GCC optimize ("Os")
+
+#include <linux/elf.h>
+#include <linux/compiler.h>
+
+struct elf_hash {
+	Elf64_Word nbuckets;
+	Elf64_Word nchains;
+	Elf64_Word data[];
+};
+
+/*
+ * These must be explicitly hidden: we need to access them via direct
+ * PC-relative relocations, not through the GOT, since we have no GOT.
+ */
+extern const char VDSO_START[] __attribute__((visibility("hidden")));
+extern const char DYN_STRTAB[] __attribute__((visibility("hidden")));
+extern Elf64_Sym DYN_SYMTAB[] __attribute__((visibility("hidden")));
+extern struct elf_hash DYN_HASH __attribute__((visibility("hidden")));
+extern Elf64_Half DYN_VERSYM[] __attribute__((visibility("hidden")));
+extern Elf64_Verdef DYN_VERDEF[] __attribute__((visibility("hidden")));
+
+/* Straight from the ELF specification. */
+static unsigned long elf_hash(const unsigned char *name)
+{
+	unsigned long h = 0, g;
+	while (*name)
+	{
+		h = (h << 4) + *name++;
+		if ((g = h & 0xf0000000) != 0)
+			h ^= g >> 24;
+		h &= ~g;
+	}
+	return h;
+}
+
+static bool strings_equal(const char *a, const char *b)
+{
+	while (true) {
+		if (*a != *b)
+			return false;
+		if (!*a)
+			return true;
+		a++;
+		b++;
+	}
+}
+
+static bool vdso_match_version(Elf64_Half ver,
+			       const char *name, Elf64_Word hash)
+{
+	/*
+	 * This is a helper function to check if the version indexed by
+	 * ver matches name (which hashes to hash).
+	 *
+	 * The version definition table is a mess, and I don't know how
+	 * to do this in better than linear time without allocating memory
+	 * to build an index.  I also don't know why the table has
+	 * variable size entries in the first place.
+	 *
+	 * For added fun, I can't find a comprehensible specification of how
+	 * to parse all the weird flags in the table.
+	 *
+	 * So I just parse the whole table every time.
+	 */
+
+	Elf64_Verdef *def = DYN_VERDEF;
+	Elf64_Verdaux *aux;
+
+	/* First step: find the version definition */
+	ver &= 0x7fff;  /* Apparently bit 15 means "hidden" */
+	while(true) {
+		if ((def->vd_flags & VER_FLG_BASE) == 0
+		    && (def->vd_ndx & 0x7fff) == ver)
+			break;
+
+		if (def->vd_next == 0)
+			return false;  /* No definition. */
+
+		def = (Elf64_Verdef *)((char *)def + def->vd_next);
+	}
+
+	/* Now figure out whether it matches. */
+	aux = (Elf64_Verdaux*)((char *)def + def->vd_aux);
+	return def->vd_hash == hash
+		&& strings_equal(name, DYN_STRTAB + aux->vda_name);
+}
+
+void *__vdso_findsym(const char *name, const char *version)
+{
+	Elf64_Word chain = DYN_HASH.data[elf_hash(name) % DYN_HASH.nbuckets];
+	const Elf64_Word *chain_next = &DYN_HASH.data[DYN_HASH.nbuckets + 2];
+	unsigned long ver_hash = elf_hash(version);
+
+	for (; chain != STN_UNDEF; chain = chain_next[chain]) {
+		Elf64_Sym *sym = &DYN_SYMTAB[chain];
+
+		/* Check for a defined global or weak function w/ right name. */
+		if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
+			continue;
+		if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
+		    ELF64_ST_BIND(sym->st_info) != STB_WEAK)
+			continue;
+		if (sym->st_shndx == SHN_UNDEF)
+			continue;
+		if (!strings_equal(name, DYN_STRTAB + sym->st_name))
+			continue;
+
+		/* Check symbol version. */
+		if (!vdso_match_version(DYN_VERSYM[chain],
+					version, ver_hash))
+			continue;
+
+		return (void *)((unsigned long)VDSO_START + sym->st_value);
+	}
+
+	return 0;
+}
+
diff --git a/arch/x86/vdso/vdso-layout.lds.S b/arch/x86/vdso/vdso-layout.lds.S
index 2ec72f6..2e8b692 100644
--- a/arch/x86/vdso/vdso-layout.lds.S
+++ b/arch/x86/vdso/vdso-layout.lds.S
@@ -8,14 +8,15 @@
 
 SECTIONS
 {
+	VDSO_START = .;
 	. = SIZEOF_HEADERS;
 
-	.hash		: { *(.hash) }			:text
+	.hash		: { DYN_HASH = .; *(.hash) }		:text
 	.gnu.hash	: { *(.gnu.hash) }
-	.dynsym		: { *(.dynsym) }
-	.dynstr		: { *(.dynstr) }
-	.gnu.version	: { *(.gnu.version) }
-	.gnu.version_d	: { *(.gnu.version_d) }
+	.dynsym		: { DYN_SYMTAB = .; *(.dynsym) }
+	.dynstr		: { DYN_STRTAB = .; *(.dynstr) }
+	.gnu.version	: { DYN_VERSYM = .; *(.gnu.version) }
+	.gnu.version_d	: { DYN_VERDEF = .; *(.gnu.version_d) }
 	.gnu.version_r	: { *(.gnu.version_r) }
 
 	.note		: { *(.note.*) }		:text	:note
diff --git a/arch/x86/vdso/vdso.lds.S b/arch/x86/vdso/vdso.lds.S
index 75e3404..9b23d4e 100644
--- a/arch/x86/vdso/vdso.lds.S
+++ b/arch/x86/vdso/vdso.lds.S
@@ -22,6 +22,7 @@ VERSION {
 		__vdso_getcpu;
 		time;
 		__vdso_time;
+		__vdso_findsym;
 	local: *;
 	};
 }
diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h
index c6eefaf..931abac 100644
--- a/arch/x86/vdso/vdso2c.h
+++ b/arch/x86/vdso/vdso2c.h
@@ -51,7 +51,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)
 	for (i = 0; dyn + i < dyn_end &&
 		     GET_LE(&dyn[i].d_tag) != DT_NULL; i++) {
 		typeof(dyn[i].d_tag) tag = GET_LE(&dyn[i].d_tag);
-		if (tag == DT_REL || tag == DT_RELSZ ||
+		if (tag == DT_REL || tag == DT_RELA || tag == DT_RELSZ ||
 		    tag == DT_RELENT || tag == DT_TEXTREL)
 			fail("vdso image contains dynamic relocations\n");
 	}
-- 
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ