diff options
author | Mike Frysinger <vapier@gentoo.org> | 2016-07-25 00:09:46 +0530 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2016-11-12 02:10:03 -0500 |
commit | 9ae5c22341a0484319c15d12cae1a46835c48379 (patch) | |
tree | a4ad7c8f02865184507b8651940e730bf3daae45 | |
parent | split out fs related helper funcs as lib code (diff) | |
download | pax-utils-9ae5c22341a0484319c15d12cae1a46835c48379.tar.gz pax-utils-9ae5c22341a0484319c15d12cae1a46835c48379.tar.bz2 pax-utils-9ae5c22341a0484319c15d12cae1a46835c48379.zip |
split out ld.so.cache & ld.so.conf parsing logic
These are getting a bit big & unwieldy for keeping inlined in scanelf.
Split them out to a dedicated file instead.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | paxinc.h | 1 | ||||
-rw-r--r-- | paxldso.c | 297 | ||||
-rw-r--r-- | paxldso.h | 69 | ||||
-rw-r--r-- | scanelf.c | 270 |
6 files changed, 374 insertions, 268 deletions
@@ -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 \ @@ -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 @@ -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; |