[CRIU] [PATCH 2/2] vdso: x86 -- Use dynamic symbols for parsing

Pavel Emelyanov xemul at parallels.com
Tue May 27 13:55:50 PDT 2014


On 05/26/2014 03:02 PM, Cyrill Gorcunov wrote:
> New vDSO are in stripped format so use dynamic
> symbols instead of sectioned ones.

How about parasite-based vdso page detection? Has it changed?

> Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
> ---
>  arch/x86/include/asm/vdso.h |  14 ---
>  arch/x86/vdso-pie.c         | 242 ++++++++++++++++++++++++++------------------
>  2 files changed, 145 insertions(+), 111 deletions(-)
> 
> diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
> index d91215c0ed71..5facf6002da2 100644
> --- a/arch/x86/include/asm/vdso.h
> +++ b/arch/x86/include/asm/vdso.h
> @@ -106,20 +106,6 @@ static inline void vdso_put_mark(void *where, unsigned long proxy_addr)
>  #define VDSO_SYMBOL_TIME_NAME		"__vdso_time"
>  
>  
> -#define DECLARE_VDSO(ident_name, symtab_name)					\

Do we need the macro itself?

> -										\
> -char ident_name[] = {								\
> -	0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,				\
> -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,				\
> -};										\
> -										\
> -char *symtab_name[VDSO_SYMBOL_MAX] = {						\
> -	[VDSO_SYMBOL_CLOCK_GETTIME]	= VDSO_SYMBOL_CLOCK_GETTIME_NAME,	\
> -	[VDSO_SYMBOL_GETCPU]		= VDSO_SYMBOL_GETCPU_NAME,		\
> -	[VDSO_SYMBOL_GETTIMEOFDAY]	= VDSO_SYMBOL_GETTIMEOFDAY_NAME,	\
> -	[VDSO_SYMBOL_TIME]		= VDSO_SYMBOL_TIME_NAME,		\
> -};
> -
>  extern struct vdso_symtable vdso_sym_rt;
>  extern u64 vdso_pfn;
>  
> diff --git a/arch/x86/vdso-pie.c b/arch/x86/vdso-pie.c
> index d04791916727..209e0bdeea21 100644
> --- a/arch/x86/vdso-pie.c
> +++ b/arch/x86/vdso-pie.c
> @@ -4,6 +4,7 @@
>  #include <string.h>
>  #include <elf.h>
>  #include <fcntl.h>
> +#include <errno.h>
>  
>  #include <sys/types.h>
>  #include <sys/stat.h>
> @@ -56,18 +57,6 @@ 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)
>  {
> @@ -75,132 +64,191 @@ static bool __ptr_oob(void *ptr, void *start, size_t size)
>  	return ptr > end || ptr < start;
>  }
>  
> +static unsigned long elf_hash(const unsigned char *name)
> +{
> +	unsigned long h = 0, g;
> +
> +	while (*name) {
> +		h = (h << 4) + *name++;
> +		g = h & 0xf0000000ul;
> +		if (g)
> +			h ^= g >> 24;
> +		h &= ~g;
> +	}
> +	return h;

Where did this come from?

> +}
> +
>  int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t)
>  {
> +	Elf64_Phdr *dynamic = NULL, *load = NULL;
>  	Elf64_Ehdr *ehdr = (void *)mem;
> -	Elf64_Shdr *shdr, *shdr_strtab;
> -	Elf64_Shdr *shdr_dynsym;
> -	Elf64_Shdr *shdr_dynstr;
> +	Elf64_Dyn *dyn_strtab = NULL;
> +	Elf64_Dyn *dyn_symtab = NULL;
> +	Elf64_Dyn *dyn_strsz = NULL;
> +	Elf64_Dyn *dyn_syment = NULL;
> +	Elf64_Dyn *dyn_hash = NULL;
> +	Elf64_Word *hash = NULL;
>  	Elf64_Phdr *phdr;
> -	Elf64_Shdr *text;
> -	Elf64_Sym *sym;
> +	Elf64_Dyn *d;
>  
> -	char *section_names, *dynsymbol_names;
> +	Elf64_Word *bucket, *chain;
> +	Elf64_Word nbucket, nchain;
>  
> -	unsigned long base = VDSO_BAD_ADDR;
> -	unsigned int i, j, k;
> +	/*
> +	 * See Elf specification.

For what?

> +	 */
> +	const char elf_ident[] = {
> +		0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
> +		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	};
>  
> -	DECLARE_VDSO(vdso_ident, vdso_symbols);
> +	const char *symtab_name[VDSO_SYMBOL_MAX] = {
> +		[VDSO_SYMBOL_CLOCK_GETTIME]	= VDSO_SYMBOL_CLOCK_GETTIME_NAME,
> +		[VDSO_SYMBOL_GETCPU]		= VDSO_SYMBOL_GETCPU_NAME,
> +		[VDSO_SYMBOL_GETTIMEOFDAY]	= VDSO_SYMBOL_GETTIMEOFDAY_NAME,
> +		[VDSO_SYMBOL_TIME]		= VDSO_SYMBOL_TIME_NAME,
> +	};

These two are moves from header. Can we split the patch?

> +
> +	char *dynsymbol_names;
> +	unsigned int i, j, k;
>  
> -	BUILD_BUG_ON(sizeof(vdso_ident) != sizeof(ehdr->e_ident));
> +	BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident));
>  
> -	pr_debug("Parsing at %lx %lx\n",
> -		 (long)mem, (long)mem + (long)size);
> +	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;
> +	if (builtin_memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) {
> +		pr_err("Elf header magic mismatch\n");
> +		return -EINVAL;
>  	}
>  
>  	/*
> -	 * Figure out base virtual address.
> +	 * We need PT_LOAD and PT_DYNAMIC here. Each once.
>  	 */
>  	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;
> +			goto err_oob;
> +		switch (phdr->p_type) {
> +		case PT_DYNAMIC:
> +			if (dynamic) {
> +				pr_err("Second PT_DYNAMIC header\n");
> +				return -EINVAL;
> +			}
> +			dynamic = phdr;
> +			break;
> +		case PT_LOAD:
> +			if (load) {
> +				pr_err("Second PT_LOAD header\n");
> +				return -EINVAL;
> +			}
> +			load = phdr;
>  			break;
>  		}
>  	}
> -	if (base != VDSO_BAD_ADDR) {
> -		pr_debug("Base address %lx\n", base);
> -	} else {
> -		pr_debug("No base address found\n");
> -		goto err;
> +
> +	if (!load || !dynamic) {
> +		pr_err("One of obligated program headers is missed\n");
> +		return -EINVAL;
>  	}
>  
> +	pr_debug("PT_LOAD p_vaddr: %lx\n", (unsigned long)load->p_vaddr);
> +
>  	/*
> -	 * Where the section names lays.
> +	 * Dynamic section should provide us the rest of

Dynamic _sections_? Or symbols?

> +	 * information needed.
>  	 */
> -	if (ehdr->e_shstrndx == SHN_UNDEF) {
> -		pr_err("Section names are not found\n");
> -		goto err;
> -	}
> +	d = (void *)&mem[dynamic->p_offset];
> +	for (i = 0; i < dynamic->p_filesz / sizeof(*d); i++, d++) {
> +		if (__ptr_oob(d, mem, size))
> +			goto err_oob;
>  
> -	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(&section_names[shdr->sh_name], mem, size))
> -			goto err;
> -
> -		if (shdr->sh_type == SHT_DYNSYM &&
> -		    builtin_strcmp(&section_names[shdr->sh_name],
> -				   ".dynsym") == 0) {
> -			shdr_dynsym = shdr;
> -		} else if (shdr->sh_type == SHT_STRTAB &&
> -			   builtin_strcmp(&section_names[shdr->sh_name],
> -					  ".dynstr") == 0) {
> -			shdr_dynstr = shdr;
> -		} else if (shdr->sh_type == SHT_PROGBITS &&
> -			   builtin_strcmp(&section_names[shdr->sh_name],
> -					  ".text") == 0) {
> -			text = shdr;
> +		if (d->d_tag == DT_NULL) {
> +			break;
> +		}else if (d->d_tag == DT_STRTAB) {

Space

> +			dyn_strtab = d;
> +			pr_debug("DT_STRTAB: %p\n", (void *)d->d_un.d_ptr);
> +		} else if (d->d_tag == DT_SYMTAB) {
> +			dyn_symtab = d;
> +			pr_debug("DT_SYMTAB: %p\n", (void *)d->d_un.d_ptr);
> +		} else if (d->d_tag == DT_STRSZ) {
> +			dyn_strsz = d;
> +			pr_debug("DT_STRSZ: %lu\n", (unsigned long)d->d_un.d_val);
> +		} else if (d->d_tag == DT_SYMENT) {
> +			dyn_syment = d;
> +			pr_debug("DT_SYMENT: %lu\n", (unsigned long)d->d_un.d_val);
> +		} else if (d->d_tag == DT_HASH) {
> +			dyn_hash = d;
> +			pr_debug("DT_HASH: %p\n", (void *)d->d_un.d_ptr);
>  		}

} else { ?

>  	}
>  
> -	if (!shdr_dynsym || !shdr_dynstr || !text) {
> -		pr_debug("No required sections found\n");
> -		goto err;
> +	if (!dyn_strtab || !dyn_symtab || !dyn_strsz || !dyn_syment || !dyn_hash) {
> +		pr_err("Not all dynamic entries are present\n");
> +		return -EINVAL;
>  	}
>  
> -	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;
> +	dynsymbol_names = &mem[dyn_strtab->d_un.d_val - load->p_vaddr];
> +	if (__ptr_oob(dynsymbol_names, mem, size))
> +		goto err_oob;
>  
> -	/*
> -	 * Walk over global symbols and choose ones we need.
> -	 */
> -	j = shdr_dynsym->sh_size / sizeof(*sym);
> -	sym = (void *)&mem[shdr_dynsym->sh_offset];
> +	hash = (void *)&mem[(unsigned long)dyn_hash->d_un.d_ptr - (unsigned long)load->p_vaddr];
> +	if (__ptr_oob(hash, mem, size))
> +		goto err_oob;
>  
> -	for (i = 0; i < j; i++, sym++) {
> -		if (__ptr_oob(sym, mem, size))
> -			goto err;
> +	nbucket = hash[0];
> +	nchain = hash[1];
> +	bucket = &hash[2];
> +	chain = &hash[nbucket + 2];
>  
> -		if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL ||
> -		    ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
> -			continue;
> +	pr_debug("nbucket %lu nchain %lu bucket %p chain %p\n",
> +		 (long)nbucket, (long)nchain, bucket, chain);
> +
> +	for (i = 0; i < ARRAY_SIZE(symtab_name); i++) {
> +		k = elf_hash((const unsigned char *)symtab_name[i]);
>  
> -		if (__ptr_oob(&dynsymbol_names[sym->st_name], mem, size))
> -			goto err;
> +		for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) {
> +			Elf64_Sym *sym = (void *)&mem[dyn_symtab->d_un.d_ptr - load->p_vaddr];
> +			char *name;
>  
> -		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;
> +			sym = &sym[j];
> +			if (__ptr_oob(sym, mem, size))
> +				continue;
> +
> +#if 0

Trash?

> +			pr_debug("type %lu bind %lu st_name %lu st_value %lx\n",
> +				 (unsigned long)ELF64_ST_TYPE(sym->st_info),
> +				 (unsigned long)ELF64_ST_BIND(sym->st_info),
> +				 (unsigned long)sym->st_name,
> +				 (unsigned long)sym->st_value);
> +#endif
> +
> +			if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC &&
> +			    ELF64_ST_BIND(sym->st_info) != STB_GLOBAL)
> +				continue;
> +
> +			name = &dynsymbol_names[sym->st_name];
> +			if (__ptr_oob(name, mem, size))
> +				continue;
> +
> +			if (builtin_strcmp(name, symtab_name[i]))
> +				continue;
> +#if 0

Trash?

> +			pr_debug("\tSymbol %s at %lx\n", name, (unsigned long)sym->st_value - load->p_vaddr);
> +#endif
> +			builtin_memcpy(t->symbols[i].name, name, sizeof(t->symbols[i].name));
> +			t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr;
> +			break;
>  		}
>  	}
> +
>  	return 0;
> -err:
> -	return -1;
> +
> +err_oob:
> +	pr_err("Corrupted Elf data\n");
> +	return -EFAULT;
>  }
>  
>  int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size)
> 




More information about the CRIU mailing list