From 7535c3909acf706ea1dfc564ee7f5628dc287e0f Mon Sep 17 00:00:00 2001 From: Alexander Kartashov Date: Tue, 18 Mar 2014 21:14:15 +0400 Subject: [PATCH] vdso.c: share the PIE part of the vDSO proxy machinery between all architectures This patch splits the file arch/x86/vdso-pie.c into machine-dependent and machine-independent parts by moving the routines vdso_fill_symtable(), vdso_proxify(), and vdso_remap() to the file pie/vdso.c. The ARM version of the routines is moved to the source pie/vdso-stub.c to provide the vDSO proxy stub implementation for architectures that don't provide the vDSO. Signed-off-by: Alexander Kartashov Looks-good-to: Cyrill Gorcunov Reviewed-by: Christopher Covington Signed-off-by: Pavel Emelyanov --- Makefile | 4 + arch/arm/include/asm/vdso.h | 3 - arch/arm/vdso-pie.c | 15 --- arch/x86/include/asm/vdso.h | 3 - arch/x86/vdso-pie.c | 237 --------------------------------- include/vdso.h | 3 + pie/Makefile | 4 +- pie/vdso-stub.c | 31 +++++ pie/vdso.c | 255 ++++++++++++++++++++++++++++++++++++ 9 files changed, 296 insertions(+), 259 deletions(-) create mode 100644 pie/vdso-stub.c create mode 100644 pie/vdso.c diff --git a/Makefile b/Makefile index 99ea75205..7e192ad1d 100644 --- a/Makefile +++ b/Makefile @@ -180,6 +180,10 @@ PROGRAM-BUILTINS += built-in.o $(ARCH_DIR)/vdso-pie.o: pie $(Q) $(MAKE) $(build)=pie $(ARCH_DIR)/vdso-pie.o PROGRAM-BUILTINS += $(ARCH_DIR)/vdso-pie.o +pie/$(VDSO_O): pie + $(Q) $(MAKE) $(build)=pie pie/$(VDSO_O) +PROGRAM-BUILTINS += pie/$(VDSO_O) + $(PROGRAM): $(SYSCALL-LIB) $(ARCH-LIB) $(PROGRAM-BUILTINS) $(E) " LINK " $@ diff --git a/arch/arm/include/asm/vdso.h b/arch/arm/include/asm/vdso.h index 0f4626fb7..b78a22c06 100644 --- a/arch/arm/include/asm/vdso.h +++ b/arch/arm/include/asm/vdso.h @@ -12,9 +12,6 @@ struct vm_area_list; #define VDSO_SYMBOL_MAX 1 extern int vdso_redirect_calls(void *base_to, void *base_from, struct vdso_symtable *to, struct vdso_symtable *from); -extern int vdso_fill_symtable(char *mem, size_t size,struct vdso_symtable *t); -extern int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size); -extern int vdso_proxify(char *who, struct vdso_symtable *sym_rt, VmaEntry *vma_entry, unsigned long vdso_rt_parked_at); extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid, struct vm_area_list *vma_area_list); diff --git a/arch/arm/vdso-pie.c b/arch/arm/vdso-pie.c index 1f094cb3a..5253d6e1a 100644 --- a/arch/arm/vdso-pie.c +++ b/arch/arm/vdso-pie.c @@ -15,18 +15,3 @@ int vdso_redirect_calls(void *base_to, void *base_from, { return 0; } - -int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t) -{ - return 0; -} - -int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size) -{ - return 0; -} - -int vdso_proxify(char *who, struct vdso_symtable *sym_rt, VmaEntry *vma, unsigned long vdso_rt_parked_at) -{ - return 0; -} diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h index ce3909b48..f231e1f87 100644 --- a/arch/x86/include/asm/vdso.h +++ b/arch/x86/include/asm/vdso.h @@ -45,9 +45,6 @@ struct parasite_ctl; struct vm_area_list; extern int vdso_redirect_calls(void *base_to, void *base_from, struct vdso_symtable *to, struct vdso_symtable *from); -extern int vdso_fill_symtable(char *mem, size_t size,struct vdso_symtable *t); -extern int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size); -extern int vdso_proxify(char *who, struct vdso_symtable *sym_rt, VmaEntry *vma_entry, unsigned long vdso_rt_parked_at); extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid, struct vm_area_list *vma_area_list); diff --git a/arch/x86/vdso-pie.c b/arch/x86/vdso-pie.c index 2d957f234..e3664f5fc 100644 --- a/arch/x86/vdso-pie.c +++ b/arch/x86/vdso-pie.c @@ -56,240 +56,3 @@ int vdso_redirect_calls(void *base_to, void *base_from, return 0; } - -static unsigned int get_symbol_index(char *symbol, char *symbols[], size_t size) -{ - unsigned int i; - - for (i = 0; symbol && i < size; i++) { - if (!builtin_strcmp(symbol, symbols[i])) - return i; - } - - return VDSO_SYMBOL_MAX; -} - -/* Check if pointer is out-of-bound */ -static bool __ptr_oob(void *ptr, void *start, size_t size) -{ - void *end = (void *)((unsigned long)start + size); - return ptr > end || ptr < start; -} - -int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t) -{ - Elf64_Ehdr *ehdr = (void *)mem; - Elf64_Shdr *shdr, *shdr_strtab; - Elf64_Shdr *shdr_dynsym; - Elf64_Shdr *shdr_dynstr; - Elf64_Phdr *phdr; - Elf64_Shdr *text; - Elf64_Sym *sym; - - char *section_names, *dynsymbol_names; - - unsigned long base = VDSO_BAD_ADDR; - unsigned int i, j, k; - - DECLARE_VDSO(vdso_ident, vdso_symbols); - - BUILD_BUG_ON(sizeof(vdso_ident) != sizeof(ehdr->e_ident)); - - pr_debug("Parsing at %lx %lx\n", - (long)mem, (long)mem + (long)size); - - /* - * Make sure it's a file we support. - */ - if (builtin_memcmp(ehdr->e_ident, vdso_ident, sizeof(vdso_ident))) { - pr_debug("Elf header magic mismatch\n"); - goto err; - } - - /* - * Figure out base virtual address. - */ - phdr = (void *)&mem[ehdr->e_phoff]; - for (i = 0; i < ehdr->e_phnum; i++, phdr++) { - if (__ptr_oob(phdr, mem, size)) - goto err; - if (phdr->p_type == PT_LOAD) { - base = phdr->p_vaddr; - break; - } - } - if (base != VDSO_BAD_ADDR) { - pr_debug("Base address %lx\n", base); - } else { - pr_debug("No base address found\n"); - goto err; - } - - /* - * Where the section names lays. - */ - if (ehdr->e_shstrndx == SHN_UNDEF) { - pr_err("Section names are not found\n"); - goto err; - } - - shdr = (void *)&mem[ehdr->e_shoff]; - shdr_strtab = &shdr[ehdr->e_shstrndx]; - if (__ptr_oob(shdr_strtab, mem, size)) - goto err; - - section_names = (void *)&mem[shdr_strtab->sh_offset]; - shdr_dynsym = shdr_dynstr = text = NULL; - - for (i = 0; i < ehdr->e_shnum; i++, shdr++) { - if (__ptr_oob(shdr, mem, size)) - goto err; - if (__ptr_oob(§ion_names[shdr->sh_name], mem, size)) - goto err; - -#if 0 - pr_debug("section: %2d -> %s\n", - i, §ion_names[shdr->sh_name]); -#endif - - if (shdr->sh_type == SHT_DYNSYM && - builtin_strcmp(§ion_names[shdr->sh_name], - ".dynsym") == 0) { - shdr_dynsym = shdr; - } else if (shdr->sh_type == SHT_STRTAB && - builtin_strcmp(§ion_names[shdr->sh_name], - ".dynstr") == 0) { - shdr_dynstr = shdr; - } else if (shdr->sh_type == SHT_PROGBITS && - builtin_strcmp(§ion_names[shdr->sh_name], - ".text") == 0) { - text = shdr; - } - } - - if (!shdr_dynsym || !shdr_dynstr || !text) { - pr_debug("No required sections found\n"); - goto err; - } - - dynsymbol_names = (void *)&mem[shdr_dynstr->sh_offset]; - if (__ptr_oob(dynsymbol_names, mem, size) || - __ptr_oob(shdr_dynsym, mem, size) || - __ptr_oob(text, mem, size)) - goto err; - - /* - * Walk over global symbols and choose ones we need. - */ - j = shdr_dynsym->sh_size / sizeof(*sym); - sym = (void *)&mem[shdr_dynsym->sh_offset]; - - for (i = 0; i < j; i++, sym++) { - if (__ptr_oob(sym, mem, size)) - goto err; - - if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL || - ELF64_ST_TYPE(sym->st_info) != STT_FUNC) - continue; - - if (__ptr_oob(&dynsymbol_names[sym->st_name], mem, size)) - goto err; - - k = get_symbol_index(&dynsymbol_names[sym->st_name], - vdso_symbols, - ARRAY_SIZE(vdso_symbols)); - if (k != VDSO_SYMBOL_MAX) { - builtin_memcpy(t->symbols[k].name, vdso_symbols[k], - sizeof(t->symbols[k].name)); - t->symbols[k].offset = (unsigned long)sym->st_value - base; -#if 0 - pr_debug("symbol: %#-16lx %2d %s\n", - t->symbols[k].offset, sym->st_shndx, t->symbols[k].name); -#endif - } - } - return 0; -err: - return -1; -} - -int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size) -{ - unsigned long addr; - - pr_debug("Remap %s %lx -> %lx\n", who, from, to); - - addr = sys_mremap(from, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, to); - if (addr != to) { - pr_err("Unable to remap %lx -> %lx %lx\n", - from, to, addr); - return -1; - } - - return 0; -} - -int vdso_proxify(char *who, struct vdso_symtable *sym_rt, VmaEntry *vma, unsigned long vdso_rt_parked_at) -{ - struct vdso_symtable s = VDSO_SYMTABLE_INIT; - size_t size = vma_entry_len(vma); - bool remap_rt = true; - - /* - * Find symbols in dumpee vdso. - */ - if (vdso_fill_symtable((void *)vma->start, size, &s)) - return -1; - - if (size == vdso_vma_size(sym_rt)) { - int i; - - for (i = 0; i < ARRAY_SIZE(s.symbols); i++) { - if (s.symbols[i].offset != sym_rt->symbols[i].offset) { - remap_rt = false; - break; - } - } - } else - remap_rt = false; - - /* - * Easy case -- the vdso from image has same offsets and size - * as runtime, so we simply remap runtime vdso to dumpee position - * without generating any proxy. - */ - if (remap_rt) { - pr_info("Runtime vdso matches dumpee, remap inplace\n"); - - if (sys_munmap((void *)vma->start, size)) { - pr_err("Failed to unmap %s\n", who); - return -1; - } - - return vdso_remap(who, vdso_rt_parked_at, vma->start, size); - } - - /* - * Now complex case -- we need to proxify calls. We redirect - * calls from dumpee vdso to runtime vdso, making dumpee - * to operate as proxy vdso. - */ - pr_info("Runtime vdso mismatches dumpee, generate proxy\n"); - - if (vdso_redirect_calls((void *)vdso_rt_parked_at, - (void *)vma->start, - sym_rt, &s)) { - pr_err("Failed to proxify dumpee contents\n"); - return -1; - } - - /* - * Put a special mark into runtime vdso, thus at next checkpoint - * routine we could detect this vdso and do not dump it, since - * it's auto-generated every new session if proxy required. - */ - sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), PROT_WRITE); - vdso_put_mark((void *)vdso_rt_parked_at, vma->start); - sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), VDSO_PROT); - return 0; -} diff --git a/include/vdso.h b/include/vdso.h index 0aa1337f4..9685078f9 100644 --- a/include/vdso.h +++ b/include/vdso.h @@ -93,5 +93,8 @@ static inline void vdso_put_mark(void *where, unsigned long proxy_addr) extern struct vdso_symtable vdso_sym_rt; extern u64 vdso_pfn; extern int vdso_init(void); +extern int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size); +extern int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t); +extern int vdso_proxify(char *who, struct vdso_symtable *sym_rt, VmaEntry *vma_entry, unsigned long vdso_rt_parked_at); #endif /* __CR_VDSO_H__ */ diff --git a/pie/Makefile b/pie/Makefile index 590280448..c75520ecb 100644 --- a/pie/Makefile +++ b/pie/Makefile @@ -4,6 +4,9 @@ targets += restorer obj-y += log-simple.o obj-y += util.o obj-y += util-fd.o +obj-y += $(VDSO_O) + +obj-e += $(ARCH_DIR)/vdso-pie.o parasite-obj-y += parasite.o parasite-asm-e += $(ARCH_DIR)/parasite-head.o @@ -11,7 +14,6 @@ parasite-libs-e += $(SYSCALL-LIB) restorer-obj-y += restorer.o restorer-obj-e += $(ARCH_DIR)/restorer.o -restorer-obj-e += $(ARCH_DIR)/vdso-pie.o restorer-libs-e += $(SYSCALL-LIB) # diff --git a/pie/vdso-stub.c b/pie/vdso-stub.c new file mode 100644 index 000000000..37393719a --- /dev/null +++ b/pie/vdso-stub.c @@ -0,0 +1,31 @@ +#include + +#include + +#include "compiler.h" +#include "vdso.h" +#include "syscall.h" +#include "log.h" + +#include "asm/string.h" + + +#ifdef LOG_PREFIX +# undef LOG_PREFIX +#endif +#define LOG_PREFIX "vdso: " + +int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size) +{ + return 0; +} + +int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t) +{ + return 0; +} + +int vdso_proxify(char *who, struct vdso_symtable *sym_rt, VmaEntry *vma, unsigned long vdso_rt_parked_at) +{ + return 0; +} diff --git a/pie/vdso.c b/pie/vdso.c new file mode 100644 index 000000000..70bcf407c --- /dev/null +++ b/pie/vdso.c @@ -0,0 +1,255 @@ +#include + +#include + +#include "compiler.h" +#include "vdso.h" +#include "syscall.h" +#include "log.h" +#include "vma.h" + +#include "asm/string.h" + + +#ifdef LOG_PREFIX +# undef LOG_PREFIX +#endif +#define LOG_PREFIX "vdso: " + + +static unsigned int get_symbol_index(char *symbol, char *symbols[], size_t size) +{ + unsigned int i; + + for (i = 0; symbol && i < size; i++) { + if (!builtin_strcmp(symbol, symbols[i])) + return i; + } + + return VDSO_SYMBOL_MAX; +} + +/* Check if pointer is out-of-bound */ +static bool __ptr_oob(void *ptr, void *start, size_t size) +{ + void *end = (void *)((unsigned long)start + size); + return ptr > end || ptr < start; +} + +int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t) +{ + Elf64_Ehdr *ehdr = (void *)mem; + Elf64_Shdr *shdr, *shdr_strtab; + Elf64_Shdr *shdr_dynsym; + Elf64_Shdr *shdr_dynstr; + Elf64_Phdr *phdr; + Elf64_Shdr *text; + Elf64_Sym *sym; + + char *section_names, *dynsymbol_names; + + unsigned long base = VDSO_BAD_ADDR; + unsigned int i, j, k; + + DECLARE_VDSO(vdso_ident, vdso_symbols); + + BUILD_BUG_ON(sizeof(vdso_ident) != sizeof(ehdr->e_ident)); + + pr_debug("Parsing at %lx %lx\n", + (long)mem, (long)mem + (long)size); + + /* + * Make sure it's a file we support. + */ + if (builtin_memcmp(ehdr->e_ident, vdso_ident, sizeof(vdso_ident))) { + pr_debug("Elf header magic mismatch\n"); + goto err; + } + + /* + * Figure out base virtual address. + */ + phdr = (void *)&mem[ehdr->e_phoff]; + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (__ptr_oob(phdr, mem, size)) + goto err; + if (phdr->p_type == PT_LOAD) { + base = phdr->p_vaddr; + break; + } + } + if (base != VDSO_BAD_ADDR) { + pr_debug("Base address %lx\n", base); + } else { + pr_debug("No base address found\n"); + goto err; + } + + /* + * Where the section names lays. + */ + if (ehdr->e_shstrndx == SHN_UNDEF) { + pr_err("Section names are not found\n"); + goto err; + } + + shdr = (void *)&mem[ehdr->e_shoff]; + shdr_strtab = &shdr[ehdr->e_shstrndx]; + if (__ptr_oob(shdr_strtab, mem, size)) + goto err; + + section_names = (void *)&mem[shdr_strtab->sh_offset]; + shdr_dynsym = shdr_dynstr = text = NULL; + + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + if (__ptr_oob(shdr, mem, size)) + goto err; + if (__ptr_oob(§ion_names[shdr->sh_name], mem, size)) + goto err; + +#if 0 + pr_debug("section: %2d -> %s\n", + i, §ion_names[shdr->sh_name]); +#endif + + if (shdr->sh_type == SHT_DYNSYM && + builtin_strcmp(§ion_names[shdr->sh_name], + ".dynsym") == 0) { + shdr_dynsym = shdr; + } else if (shdr->sh_type == SHT_STRTAB && + builtin_strcmp(§ion_names[shdr->sh_name], + ".dynstr") == 0) { + shdr_dynstr = shdr; + } else if (shdr->sh_type == SHT_PROGBITS && + builtin_strcmp(§ion_names[shdr->sh_name], + ".text") == 0) { + text = shdr; + } + } + + if (!shdr_dynsym || !shdr_dynstr || !text) { + pr_debug("No required sections found\n"); + goto err; + } + + dynsymbol_names = (void *)&mem[shdr_dynstr->sh_offset]; + if (__ptr_oob(dynsymbol_names, mem, size) || + __ptr_oob(shdr_dynsym, mem, size) || + __ptr_oob(text, mem, size)) + goto err; + + /* + * Walk over global symbols and choose ones we need. + */ + j = shdr_dynsym->sh_size / sizeof(*sym); + sym = (void *)&mem[shdr_dynsym->sh_offset]; + + for (i = 0; i < j; i++, sym++) { + if (__ptr_oob(sym, mem, size)) + goto err; + + if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL || + ELF64_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + + if (__ptr_oob(&dynsymbol_names[sym->st_name], mem, size)) + goto err; + + k = get_symbol_index(&dynsymbol_names[sym->st_name], + vdso_symbols, + ARRAY_SIZE(vdso_symbols)); + if (k != VDSO_SYMBOL_MAX) { + builtin_memcpy(t->symbols[k].name, vdso_symbols[k], + sizeof(t->symbols[k].name)); + t->symbols[k].offset = (unsigned long)sym->st_value - base; +#if 0 + pr_debug("symbol: %#-16lx %2d %s\n", + t->symbols[k].offset, sym->st_shndx, t->symbols[k].name); +#endif + } + } + return 0; +err: + return -1; +} + +int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size) +{ + unsigned long addr; + + pr_debug("Remap %s %lx -> %lx\n", who, from, to); + + addr = sys_mremap(from, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, to); + if (addr != to) { + pr_err("Unable to remap %lx -> %lx %lx\n", + from, to, addr); + return -1; + } + + return 0; +} + +int vdso_proxify(char *who, struct vdso_symtable *sym_rt, VmaEntry *vma, unsigned long vdso_rt_parked_at) +{ + struct vdso_symtable s = VDSO_SYMTABLE_INIT; + size_t size = vma_entry_len(vma); + bool remap_rt = true; + + /* + * Find symbols in dumpee vdso. + */ + if (vdso_fill_symtable((void *)vma->start, size, &s)) + return -1; + + if (size == vdso_vma_size(sym_rt)) { + int i; + + for (i = 0; i < ARRAY_SIZE(s.symbols); i++) { + if (s.symbols[i].offset != sym_rt->symbols[i].offset) { + remap_rt = false; + break; + } + } + } else + remap_rt = false; + + /* + * Easy case -- the vdso from image has same offsets and size + * as runtime, so we simply remap runtime vdso to dumpee position + * without generating any proxy. + */ + if (remap_rt) { + pr_info("Runtime vdso matches dumpee, remap inplace\n"); + + if (sys_munmap((void *)vma->start, size)) { + pr_err("Failed to unmap %s\n", who); + return -1; + } + + return vdso_remap(who, vdso_rt_parked_at, vma->start, size); + } + + /* + * Now complex case -- we need to proxify calls. We redirect + * calls from dumpee vdso to runtime vdso, making dumpee + * to operate as proxy vdso. + */ + pr_info("Runtime vdso mismatches dumpee, generate proxy\n"); + + if (vdso_redirect_calls((void *)vdso_rt_parked_at, + (void *)vma->start, + sym_rt, &s)) { + pr_err("Failed to proxify dumpee contents\n"); + return -1; + } + + /* + * Put a special mark into runtime vdso, thus at next checkpoint + * routine we could detect this vdso and do not dump it, since + * it's auto-generated every new session if proxy required. + */ + sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), PROT_WRITE); + vdso_put_mark((void *)vdso_rt_parked_at, vma->start); + sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), VDSO_PROT); + return 0; +}