aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--Makefile.am3
-rw-r--r--paxinc.h1
-rw-r--r--paxldso.c297
-rw-r--r--paxldso.h69
-rw-r--r--scanelf.c270
6 files changed, 374 insertions, 268 deletions
diff --git a/Makefile b/Makefile
index 91b1536..569e924 100644
--- a/Makefile
+++ b/Makefile
@@ -66,7 +66,7 @@ override CPPFLAGS += -DVCSID='"$(VCSID)"'
####################################################################
ELF_TARGETS = scanelf dumpelf $(shell echo | $(CC) -dM -E - | grep -q __svr4__ || echo pspax)
-ELF_OBJS = paxelf.o
+ELF_OBJS = paxelf.o paxldso.o
MACH_TARGETS = scanmacho
MACH_OBJS = paxmacho.o
COMMON_OBJS = paxinc.o security.o xfuncs.o
diff --git a/Makefile.am b/Makefile.am
index 0c1945a..a2c02d9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,6 +11,7 @@ libpaxutils_la_LDFLAGS = -no-undefined
libpaxutils_la_SOURCES = \
paxelf.c \
paxinc.c \
+ paxldso.c \
paxmacho.c \
security.c \
xfuncs.c
@@ -81,6 +82,8 @@ EXTRA_DIST += \
paxelf.h \
paxinc.c \
paxinc.h \
+ paxldso.c \
+ paxldso.h \
paxmacho.c \
paxmacho.h \
porting.h \
diff --git a/paxinc.h b/paxinc.h
index f761b2e..82f7d1f 100644
--- a/paxinc.h
+++ b/paxinc.h
@@ -31,6 +31,7 @@
/* ELF love */
#include "elf.h"
#include "paxelf.h"
+#include "paxldso.h"
/* Mach-O love */
#include "macho.h"
diff --git a/paxldso.c b/paxldso.c
new file mode 100644
index 0000000..638db77
--- /dev/null
+++ b/paxldso.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2003-2016 Gentoo Foundation
+ * Distributed under the terms of the GNU General Public License v2
+ *
+ * Copyright 2003-2012 Ned Ludd - <solar@gentoo.org>
+ * Copyright 2004-2016 Mike Frysinger - <vapier@gentoo.org>
+ */
+
+#include "paxinc.h"
+
+/*
+ * ld.so.cache logic
+ */
+
+#if PAX_LDSO_CACHE
+
+static void *ldcache = NULL;
+static size_t ldcache_size = 0;
+
+/* Defines can be seen in glibc's sysdeps/generic/ldconfig.h */
+#define LDSO_CACHE_MAGIC "ld.so-"
+#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
+#define LDSO_CACHE_VER "1.7.0"
+#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
+#define FLAG_ANY -1
+#define FLAG_TYPE_MASK 0x00ff
+#define FLAG_LIBC4 0x0000
+#define FLAG_ELF 0x0001
+#define FLAG_ELF_LIBC5 0x0002
+#define FLAG_ELF_LIBC6 0x0003
+#define FLAG_REQUIRED_MASK 0xff00
+#define FLAG_SPARC_LIB64 0x0100
+#define FLAG_IA64_LIB64 0x0200
+#define FLAG_X8664_LIB64 0x0300
+#define FLAG_S390_LIB64 0x0400
+#define FLAG_POWERPC_LIB64 0x0500
+#define FLAG_MIPS64_LIBN32 0x0600
+#define FLAG_MIPS64_LIBN64 0x0700
+#define FLAG_X8664_LIBX32 0x0800
+#define FLAG_ARM_LIBHF 0x0900
+#define FLAG_AARCH64_LIB64 0x0a00
+
+#if defined(__GLIBC__) || defined(__UCLIBC__)
+
+char *ldso_cache_lookup_lib(elfobj *elf, const char *fname)
+{
+ int fd;
+ char *strs;
+ static char buf[__PAX_UTILS_PATH_MAX] = "";
+ const char *cachefile = root_rel_path("/etc/ld.so.cache");
+ struct stat st;
+
+ typedef struct {
+ char magic[LDSO_CACHE_MAGIC_LEN];
+ char version[LDSO_CACHE_VER_LEN];
+ int nlibs;
+ } header_t;
+ header_t *header;
+
+ typedef struct {
+ int flags;
+ int sooffset;
+ int liboffset;
+ } libentry_t;
+ libentry_t *libent;
+
+ if (fname == NULL)
+ return NULL;
+
+ if (ldcache == NULL) {
+ if (fstatat(root_fd, cachefile, &st, 0))
+ return NULL;
+
+ fd = openat(root_fd, cachefile, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+
+ /* cache these values so we only map/unmap the cache file once */
+ ldcache_size = st.st_size;
+ header = ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (ldcache == MAP_FAILED) {
+ ldcache = NULL;
+ return NULL;
+ }
+
+ if (memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) ||
+ memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
+ {
+ munmap(ldcache, ldcache_size);
+ ldcache = NULL;
+ return NULL;
+ }
+ } else
+ header = ldcache;
+
+ libent = ldcache + sizeof(header_t);
+ strs = (char *) &libent[header->nlibs];
+
+ for (fd = 0; fd < header->nlibs; ++fd) {
+ /* This should be more fine grained, but for now we assume that
+ * diff arches will not be cached together, and we ignore the
+ * the different multilib mips cases.
+ */
+ if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
+ continue;
+ if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
+ continue;
+
+ if (strcmp(fname, strs + libent[fd].sooffset) != 0)
+ continue;
+
+ /* Return first hit because that is how the ldso rolls */
+ strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
+ break;
+ }
+
+ return buf;
+}
+
+#elif defined(__NetBSD__)
+
+char *ldso_cache_lookup_lib(elfobj *elf, const char *fname)
+{
+ static char buf[__PAX_UTILS_PATH_MAX] = "";
+ static struct stat st;
+ size_t n;
+ char *ldpath;
+
+ array_for_each(ldpath, n, ldpath) {
+ if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", ldpath, fname) >= sizeof(buf))
+ continue; /* if the pathname is too long, or something went wrong, ignore */
+
+ if (stat(buf, &st) != 0)
+ continue; /* if the lib doesn't exist in *ldpath, look further */
+
+ /* NetBSD doesn't actually do sanity checks, it just loads the file
+ * and if that doesn't work, continues looking in other directories.
+ * This cannot easily be safely emulated, unfortunately. For now,
+ * just assume that if it exists, it's a valid library. */
+
+ return buf;
+ }
+
+ /* not found in any path */
+ return NULL;
+}
+
+#endif
+
+static void ldso_cache_cleanup(void)
+{
+ if (ldcache != NULL)
+ munmap(ldcache, ldcache_size);
+}
+
+#else
+# define ldso_cache_cleanup()
+#endif /* PAX_LDSO_CACHE */
+
+/*
+ * ld.so.conf logic
+ */
+
+#if PAX_LDSO_CONFIG
+
+static array_t _ldpaths = array_init_decl;
+array_t *ldpaths = &_ldpaths;
+
+#if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
+
+int ldso_config_load(const char *fname)
+{
+ FILE *fp = NULL;
+ char *p, *path;
+ size_t len;
+ int curr_fd = -1;
+
+ fp = fopenat_r(root_fd, root_rel_path(fname));
+ if (fp == NULL)
+ return -1;
+
+ path = NULL;
+ len = 0;
+ while (getline(&path, &len, fp) != -1) {
+ if ((p = strrchr(path, '\r')) != NULL)
+ *p = 0;
+ if ((p = strchr(path, '\n')) != NULL)
+ *p = 0;
+
+ /* recursive includes of the same file will make this segfault. */
+ if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
+ glob_t gl;
+ size_t x;
+ const char *gpath;
+
+ /* re-use existing path buffer ... need to be creative */
+ if (path[8] != '/')
+ gpath = memcpy(path + 3, "/etc/", 5);
+ else
+ gpath = path + 8;
+ if (root_fd != AT_FDCWD) {
+ if (curr_fd == -1) {
+ curr_fd = open(".", O_RDONLY|O_CLOEXEC);
+ if (fchdir(root_fd))
+ errp("unable to change to root dir");
+ }
+ gpath = root_rel_path(gpath);
+ }
+
+ if (glob(gpath, 0, NULL, &gl) == 0) {
+ for (x = 0; x < gl.gl_pathc; ++x) {
+ /* try to avoid direct loops */
+ if (strcmp(gl.gl_pathv[x], fname) == 0)
+ continue;
+ ldso_config_load(gl.gl_pathv[x]);
+ }
+ globfree(&gl);
+ }
+
+ /* failed globs are ignored by glibc */
+ continue;
+ }
+
+ if (*path != '/')
+ continue;
+
+ xarraypush_str(ldpaths, path);
+ }
+ free(path);
+
+ fclose(fp);
+
+ if (curr_fd != -1) {
+ if (fchdir(curr_fd))
+ {/* don't care */}
+ close(curr_fd);
+ }
+
+ return 0;
+}
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+
+int ldso_config_load(const char *fname)
+{
+ FILE *fp = NULL;
+ char *b = NULL, *p;
+ struct elfhints_hdr hdr;
+
+ fp = fopenat_r(root_fd, root_rel_path(fname));
+ if (fp == NULL)
+ return -1;
+
+ if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
+ hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
+ fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
+ {
+ fclose(fp);
+ return -1;
+ }
+
+ b = xmalloc(hdr.dirlistlen + 1);
+ if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
+ fclose(fp);
+ free(b);
+ return -1;
+ }
+
+ while ((p = strsep(&b, ":"))) {
+ if (*p == '\0')
+ continue;
+ xarraypush_str(ldpaths, p);
+ }
+
+ free(b);
+ fclose(fp);
+ return 0;
+}
+
+#endif
+
+static void ldso_config_cleanup(void)
+{
+ xarrayfree(ldpaths);
+}
+
+#else
+# define ldso_config_cleanup()
+#endif /* PAX_LDSO_CONFIG */
+
+void paxldso_cleanup(void)
+{
+ ldso_cache_cleanup();
+ ldso_config_cleanup();
+}
diff --git a/paxldso.h b/paxldso.h
new file mode 100644
index 0000000..2fdc540
--- /dev/null
+++ b/paxldso.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2003-2016 Gentoo Foundation
+ * Distributed under the terms of the GNU General Public License v2
+ *
+ * Copyright 2003-2012 Ned Ludd - <solar@gentoo.org>
+ * Copyright 2004-2016 Mike Frysinger - <vapier@gentoo.org>
+ */
+
+#ifndef _PAX_LDSO_H
+#define _PAX_LDSO_H
+
+/*
+ * ld.so.cache logic
+ */
+
+#if !defined(__GLIBC__) && \
+ !defined(__UCLIBC__) && \
+ !defined(__NetBSD__)
+# ifdef __ELF__
+# warning Cache support not implemented for your target
+# endif
+# define PAX_LDSO_CACHE 0
+#else
+# define PAX_LDSO_CACHE 1
+#endif
+
+#if PAX_LDSO_CACHE
+extern char *ldso_cache_lookup_lib(elfobj *elf, const char *fname);
+#else
+static inline char *ldso_cache_lookup_lib(elfobj *elf, const char *fname)
+{
+ return NULL;
+}
+#endif
+
+/*
+ * ld.so.conf logic
+ */
+
+#if !defined(__GLIBC__) && \
+ !defined(__UCLIBC__) && \
+ !defined(__NetBSD__) && \
+ !defined(__FreeBSD__) && \
+ !defined(__DragonFly__)
+# ifdef __ELF__
+# warning Cache config support not implemented for your target
+# endif
+# define PAX_LDSO_CONFIG 0
+#else
+# define PAX_LDSO_CONFIG 1
+#endif
+
+#if PAX_LDSO_CONFIG
+extern array_t *ldpaths;
+extern int ldso_config_load(const char *fname);
+#else
+static inline int ldso_config_load(const char *fname)
+{
+ return 0;
+}
+#endif
+
+#if PAX_LDSO_CACHE || PAX_LDSO_CONFIG
+extern void paxldso_cleanup(void);
+#else
+# define paxldso_cleanup()
+#endif
+
+#endif
diff --git a/scanelf.c b/scanelf.c
index 89c9695..171093a 100644
--- a/scanelf.c
+++ b/scanelf.c
@@ -17,7 +17,6 @@ static int file_matches_list(const char *filename, char **matchlist);
/* variables to control behavior */
static array_t _match_etypes = array_init_decl, *match_etypes = &_match_etypes;
-static array_t _ldpaths = array_init_decl, *ldpaths = &_ldpaths;
static char scan_ldpath = 0;
static char scan_envpath = 0;
static char scan_symlink = 1;
@@ -63,8 +62,6 @@ static char **qa_wx_load = NULL;
static int match_bits = 0;
static unsigned int match_perms = 0;
-static void *ldcache = NULL;
-static size_t ldcache_size = 0;
static unsigned long setpax = 0UL;
static const char *objdump;
@@ -833,144 +830,6 @@ static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_
xstrcat(ret, " - ", ret_len);
}
-/* Defines can be seen in glibc's sysdeps/generic/ldconfig.h */
-#define LDSO_CACHE_MAGIC "ld.so-"
-#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
-#define LDSO_CACHE_VER "1.7.0"
-#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
-#define FLAG_ANY -1
-#define FLAG_TYPE_MASK 0x00ff
-#define FLAG_LIBC4 0x0000
-#define FLAG_ELF 0x0001
-#define FLAG_ELF_LIBC5 0x0002
-#define FLAG_ELF_LIBC6 0x0003
-#define FLAG_REQUIRED_MASK 0xff00
-#define FLAG_SPARC_LIB64 0x0100
-#define FLAG_IA64_LIB64 0x0200
-#define FLAG_X8664_LIB64 0x0300
-#define FLAG_S390_LIB64 0x0400
-#define FLAG_POWERPC_LIB64 0x0500
-#define FLAG_MIPS64_LIBN32 0x0600
-#define FLAG_MIPS64_LIBN64 0x0700
-#define FLAG_X8664_LIBX32 0x0800
-#define FLAG_ARM_LIBHF 0x0900
-#define FLAG_AARCH64_LIB64 0x0a00
-
-#if defined(__GLIBC__) || defined(__UCLIBC__)
-
-static char *lookup_cache_lib(elfobj *elf, const char *fname)
-{
- int fd;
- char *strs;
- static char buf[__PAX_UTILS_PATH_MAX] = "";
- const char *cachefile = root_rel_path("/etc/ld.so.cache");
- struct stat st;
-
- typedef struct {
- char magic[LDSO_CACHE_MAGIC_LEN];
- char version[LDSO_CACHE_VER_LEN];
- int nlibs;
- } header_t;
- header_t *header;
-
- typedef struct {
- int flags;
- int sooffset;
- int liboffset;
- } libentry_t;
- libentry_t *libent;
-
- if (fname == NULL)
- return NULL;
-
- if (ldcache == NULL) {
- if (fstatat(root_fd, cachefile, &st, 0))
- return NULL;
-
- fd = openat(root_fd, cachefile, O_RDONLY);
- if (fd == -1)
- return NULL;
-
- /* cache these values so we only map/unmap the cache file once */
- ldcache_size = st.st_size;
- header = ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
- close(fd);
-
- if (ldcache == MAP_FAILED) {
- ldcache = NULL;
- return NULL;
- }
-
- if (memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) ||
- memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
- {
- munmap(ldcache, ldcache_size);
- ldcache = NULL;
- return NULL;
- }
- } else
- header = ldcache;
-
- libent = ldcache + sizeof(header_t);
- strs = (char *) &libent[header->nlibs];
-
- for (fd = 0; fd < header->nlibs; ++fd) {
- /* This should be more fine grained, but for now we assume that
- * diff arches will not be cached together, and we ignore the
- * the different multilib mips cases.
- */
- if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
- continue;
- if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
- continue;
-
- if (strcmp(fname, strs + libent[fd].sooffset) != 0)
- continue;
-
- /* Return first hit because that is how the ldso rolls */
- strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
- break;
- }
-
- return buf;
-}
-
-#elif defined(__NetBSD__)
-static char *lookup_cache_lib(elfobj *elf, const char *fname)
-{
- static char buf[__PAX_UTILS_PATH_MAX] = "";
- static struct stat st;
- size_t n;
- char *ldpath;
-
- array_for_each(ldpath, n, ldpath) {
- if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", ldpath, fname) >= sizeof(buf))
- continue; /* if the pathname is too long, or something went wrong, ignore */
-
- if (stat(buf, &st) != 0)
- continue; /* if the lib doesn't exist in *ldpath, look further */
-
- /* NetBSD doesn't actually do sanity checks, it just loads the file
- * and if that doesn't work, continues looking in other directories.
- * This cannot easily be safely emulated, unfortunately. For now,
- * just assume that if it exists, it's a valid library. */
-
- return buf;
- }
-
- /* not found in any path */
- return NULL;
-}
-#else
-#ifdef __ELF__
-#warning Cache support not implemented for your target
-#endif
-static char *lookup_cache_lib(elfobj *elf, const char *fname)
-{
- return NULL;
-}
-#endif
-
static char *lookup_config_lib(const char *fname)
{
static char buf[__PAX_UTILS_PATH_MAX] = "";
@@ -1036,7 +895,7 @@ static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char
if ((p = lookup_config_lib(needed)) != NULL) \
needed = p; \
} else if (use_ldcache) { \
- if ((p = lookup_cache_lib(elf, needed)) != NULL) \
+ if ((p = ldso_cache_lookup_lib(elf, needed)) != NULL) \
needed = p; \
} \
xstrcat(ret, needed, ret_len); \
@@ -1865,134 +1724,13 @@ static int scanelf_from_file(const char *filename)
return ret;
}
-#if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
-
-static int _load_ld_cache_config(const char *fname)
-{
- FILE *fp = NULL;
- char *p, *path;
- size_t len;
- int curr_fd = -1;
-
- fp = fopenat_r(root_fd, root_rel_path(fname));
- if (fp == NULL)
- return -1;
-
- path = NULL;
- len = 0;
- while (getline(&path, &len, fp) != -1) {
- if ((p = strrchr(path, '\r')) != NULL)
- *p = 0;
- if ((p = strchr(path, '\n')) != NULL)
- *p = 0;
-
- /* recursive includes of the same file will make this segfault. */
- if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
- glob_t gl;
- size_t x;
- const char *gpath;
-
- /* re-use existing path buffer ... need to be creative */
- if (path[8] != '/')
- gpath = memcpy(path + 3, "/etc/", 5);
- else
- gpath = path + 8;
- if (root_fd != AT_FDCWD) {
- if (curr_fd == -1) {
- curr_fd = open(".", O_RDONLY|O_CLOEXEC);
- if (fchdir(root_fd))
- errp("unable to change to root dir");
- }
- gpath = root_rel_path(gpath);
- }
-
- if (glob(gpath, 0, NULL, &gl) == 0) {
- for (x = 0; x < gl.gl_pathc; ++x) {
- /* try to avoid direct loops */
- if (strcmp(gl.gl_pathv[x], fname) == 0)
- continue;
- _load_ld_cache_config(gl.gl_pathv[x]);
- }
- globfree(&gl);
- }
-
- /* failed globs are ignored by glibc */
- continue;
- }
-
- if (*path != '/')
- continue;
-
- xarraypush_str(ldpaths, path);
- }
- free(path);
-
- fclose(fp);
-
- if (curr_fd != -1) {
- if (fchdir(curr_fd))
- {/* don't care */}
- close(curr_fd);
- }
-
- return 0;
-}
-
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-
-static int _load_ld_cache_config(const char *fname)
-{
- FILE *fp = NULL;
- char *b = NULL, *p;
- struct elfhints_hdr hdr;
-
- fp = fopenat_r(root_fd, root_rel_path(fname));
- if (fp == NULL)
- return -1;
-
- if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
- hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
- fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
- {
- fclose(fp);
- return -1;
- }
-
- b = xmalloc(hdr.dirlistlen + 1);
- if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
- fclose(fp);
- free(b);
- return -1;
- }
-
- while ((p = strsep(&b, ":"))) {
- if (*p == '\0')
- continue;
- xarraypush_str(ldpaths, p);
- }
-
- free(b);
- fclose(fp);
- return 0;
-}
-
-#else
-#ifdef __ELF__
-#warning Cache config support not implemented for your target
-#endif
-static int _load_ld_cache_config(const char *fname)
-{
- return 0;
-}
-#endif
-
static void load_ld_cache_config(const char *fname)
{
bool scan_l, scan_ul, scan_ull;
size_t n;
const char *ldpath;
- _load_ld_cache_config(fname);
+ ldso_config_load(fname);
scan_l = scan_ul = scan_ull = false;
array_for_each(ldpaths, n, ldpath) {
@@ -2469,7 +2207,6 @@ static int parseargs(int argc, char *argv[])
if (PAX_UTILS_CLEANUP) {
/* clean up */
- xarrayfree(ldpaths);
xarrayfree(find_sym_arr);
xarrayfree(find_lib_arr);
xarrayfree(find_section_arr);
@@ -2484,8 +2221,7 @@ static int parseargs(int argc, char *argv[])
xarrayfree(find_sym_regex_arr);
}
- if (ldcache != 0)
- munmap(ldcache, ldcache_size);
+ paxldso_cleanup();
}
return ret;