[CRIU] [PATCH] zdtm: Add vdso01 test case
Andrew Vagin
avagin at parallels.com
Mon Jun 23 11:34:40 PDT 2014
On Mon, Jun 23, 2014 at 05:00:28PM +0400, Cyrill Gorcunov wrote:
> It parses vDSO in memory (just like CRIU does) and
> then use direct calls to vDSO entries instead of
> .plt/.got bundle. The reason for that -- I must
> be sure we're able to proceed calls without relying
> on libc anyhow.
>
> Note the test is x86-64 specific so I don't turn in
> on in test suite by default.
Jenkins will not execute it...
We can use a macros to determine x86_64.
#if __x86_64__
/* 64-bit */
#endif
>
> Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
> ---
> test/zdtm/live/static/Makefile | 1 +
> test/zdtm/live/static/vdso01.c | 421 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 422 insertions(+)
> create mode 100644 test/zdtm/live/static/vdso01.c
>
> diff --git a/test/zdtm/live/static/Makefile b/test/zdtm/live/static/Makefile
> index 4d275f5fae8e..8605f435e28c 100644
> --- a/test/zdtm/live/static/Makefile
> +++ b/test/zdtm/live/static/Makefile
> @@ -45,6 +45,7 @@ TST_NOFILE = \
> pthread00 \
> pthread01 \
> vdso00 \
> + vdso01 \
> utsname \
> pstree \
> sockets01 \
> diff --git a/test/zdtm/live/static/vdso01.c b/test/zdtm/live/static/vdso01.c
> new file mode 100644
> index 000000000000..dd5ee3aa961a
> --- /dev/null
> +++ b/test/zdtm/live/static/vdso01.c
> @@ -0,0 +1,421 @@
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <elf.h>
> +#include <time.h>
> +
> +#include <sys/time.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +
> +#include "zdtmtst.h"
> +
> +const char *test_doc = "Check if we can use vDSO using direct vDSO calls\n";
> +const char *test_author = "Cyrill Gorcunov <gorcunov at openvz.org";
> +
> +typedef int (__vdso_clock_gettime_t)(clockid_t clock, struct timespec *ts);
> +typedef long (__vdso_getcpu_t)(unsigned *cpu, unsigned *node, void *unused);
> +typedef int (__vdso_gettimeofday_t)(struct timeval *tv, struct timezone *tz);
> +typedef time_t (__vdso_time_t)(time_t *t);
> +
> +#define TIME_DELTA_SEC (3)
> +
> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
> +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
> +
> +#define VDSO_BAD_ADDR (-1ul)
> +
> +struct vdso_symbol {
> + char name[32];
> + unsigned long offset;
> +};
> +
> +#define VDSO_SYMBOL_INIT { .offset = VDSO_BAD_ADDR, }
> +
> +/* Check if symbol present in symtable */
> +static inline bool vdso_symbol_empty(struct vdso_symbol *s)
> +{
> + return s->offset == VDSO_BAD_ADDR && s->name[0] == '\0';
> +}
> +
> +enum {
> + VDSO_SYMBOL_CLOCK_GETTIME,
> + VDSO_SYMBOL_GETCPU,
> + VDSO_SYMBOL_GETTIMEOFDAY,
> + VDSO_SYMBOL_TIME,
> +
> + VDSO_SYMBOL_MAX
> +};
> +
> +#define VDSO_SYMBOL_CLOCK_GETTIME_NAME "__vdso_clock_gettime"
> +#define VDSO_SYMBOL_GETCPU_NAME "__vdso_getcpu"
> +#define VDSO_SYMBOL_GETTIMEOFDAY_NAME "__vdso_gettimeofday"
> +#define VDSO_SYMBOL_TIME_NAME "__vdso_time"
> +
> +struct vdso_symtable {
> + unsigned long vma_start;
> + unsigned long vma_end;
> + struct vdso_symbol symbols[VDSO_SYMBOL_MAX];
> +};
> +
> +#define VDSO_SYMTABLE_INIT \
> + { \
> + .vma_start = VDSO_BAD_ADDR, \
> + .vma_end = VDSO_BAD_ADDR, \
> + .symbols = { \
> + [0 ... VDSO_SYMBOL_MAX - 1] = \
> + (struct vdso_symbol)VDSO_SYMBOL_INIT, \
> + }, \
> + }
> +
> +static bool __ptr_oob(void *ptr, void *start, size_t size)
> +{
> + void *end = (void *)((unsigned long)start + 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;
> +}
> +
> +static 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_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_Dyn *d;
> +
> + Elf64_Word *bucket, *chain;
> + Elf64_Word nbucket, nchain;
> +
> + /*
> + * See Elf specification for this magic values.
> + */
> + const char elf_ident[] = {
> + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + };
> +
> + const char *vdso_symbols[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,
> + };
> +
> + char *dynsymbol_names;
> + unsigned int i, j, k;
> +
> + BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident));
> +
> + test_msg("Parsing at %lx %lx\n", (long)mem, (long)mem + (long)size);
> +
> + /*
> + * Make sure it's a file we support.
> + */
> + if (memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) {
> + err("Elf header magic mismatch\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * 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_oob;
> + switch (phdr->p_type) {
> + case PT_DYNAMIC:
> + if (dynamic) {
> + err("Second PT_DYNAMIC header\n");
> + return -EINVAL;
> + }
> + dynamic = phdr;
> + break;
> + case PT_LOAD:
> + if (load) {
> + err("Second PT_LOAD header\n");
> + return -EINVAL;
> + }
> + load = phdr;
> + break;
> + }
> + }
> +
> + if (!load || !dynamic) {
> + err("One of obligated program headers is missed\n");
> + return -EINVAL;
> + }
> +
> + test_msg("PT_LOAD p_vaddr: %lx\n", (unsigned long)load->p_vaddr);
> +
> + /*
> + * Dynamic section tags should provide us the rest of information
> + * needed. Note that we're interested in a small set of tags.
> + */
> + 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;
> +
> + if (d->d_tag == DT_NULL) {
> + break;
> + } else if (d->d_tag == DT_STRTAB) {
> + dyn_strtab = d;
> + } else if (d->d_tag == DT_SYMTAB) {
> + dyn_symtab = d;
> + } else if (d->d_tag == DT_STRSZ) {
> + dyn_strsz = d;
> + } else if (d->d_tag == DT_SYMENT) {
> + dyn_syment = d;
> + } else if (d->d_tag == DT_HASH) {
> + dyn_hash = d;
> + }
> + }
> +
> + if (!dyn_strtab || !dyn_symtab || !dyn_strsz || !dyn_syment || !dyn_hash) {
> + err("Not all dynamic entries are present\n");
> + return -EINVAL;
> + }
> +
> + dynsymbol_names = &mem[dyn_strtab->d_un.d_val - load->p_vaddr];
> + if (__ptr_oob(dynsymbol_names, mem, size))
> + goto err_oob;
> +
> + 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;
> +
> + nbucket = hash[0];
> + nchain = hash[1];
> + bucket = &hash[2];
> + chain = &hash[nbucket + 2];
> +
> + test_msg("nbucket %lu nchain %lu bucket %p chain %p\n",
> + (long)nbucket, (long)nchain, bucket, chain);
> +
> + for (i = 0; i < ARRAY_SIZE(vdso_symbols); i++) {
> + k = elf_hash((const unsigned char *)vdso_symbols[i]);
> +
> + 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;
> +
> + sym = &sym[j];
> + if (__ptr_oob(sym, mem, size))
> + continue;
> +
> + 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 (strcmp(name, vdso_symbols[i]))
> + continue;
> +
> + memcpy(t->symbols[i].name, name, sizeof(t->symbols[i].name));
> + t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr;
> + test_msg("symbol %s offset %lx\n", t->symbols[i].name, t->symbols[i].offset);
> + break;
> + }
> + }
> +
> + return 0;
> +
> +err_oob:
> + err("Corrupted Elf data\n");
> + return -EFAULT;
> +}
> +
> +static int vdso_fill_self_symtable(struct vdso_symtable *s)
> +{
> + char buf[512];
> + int ret = -1;
> + FILE *maps;
> +
> + *s = (struct vdso_symtable)VDSO_SYMTABLE_INIT;
> +
> + maps = fopen("/proc/self/maps", "r");
> + if (!maps) {
> + err("Can't open self-vma");
> + return -1;
> + }
> +
> + while (fgets(buf, sizeof(buf), maps)) {
> + unsigned long start, end;
> +
> + if (!strstr(buf, "[vdso]"))
> + continue;
> +
> + ret = sscanf(buf, "%lx-%lx", &start, &end);
> + if (ret != 2) {
> + ret = -1;
> + err("Can't find vDSO bounds");
> + goto err;
> + }
> +
> + s->vma_start = start;
> + s->vma_end = end;
> +
> + ret = vdso_fill_symtable((void *)start, end - start, s);
> + break;
> + }
> +
> + test_msg("[vdso] %lx-%lx\n", s->vma_start, s->vma_end);
> +err:
> + fclose(maps);
> + return ret;
> +}
> +
> +static int vdso_clock_gettime_handler(void *func)
> +{
> + __vdso_clock_gettime_t *vdso_clock_gettime = func;
> + struct timespec ts1, ts2;
> +
> + clock_gettime(CLOCK_REALTIME, &ts1);
> + vdso_clock_gettime(CLOCK_REALTIME, &ts2);
> +
> + test_msg("clock_gettime: tv_sec %li vdso_clock_gettime: tv_sec %li\n",
> + ts1.tv_sec, ts2.tv_sec);
> +
> + if (abs(ts1.tv_sec - ts2.tv_sec) > TIME_DELTA_SEC) {
> + err("Delta is too big");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int vdso_getcpu_handler(void *func)
> +{
> + __vdso_getcpu_t *vdso_getcpu = func;
> + unsigned cpu, node;
> +
> + vdso_getcpu(&cpu, &node, NULL);
> + test_msg("vdso_getcpu: cpu %d node %d\n", cpu, node);
> +
> + return 0;
> +}
> +
> +static int vdso_gettimeofday_handler(void *func)
> +{
> + __vdso_gettimeofday_t *vdso_gettimeofday = func;
> + struct timeval tv1, tv2;
> + struct timezone tz;
> +
> + gettimeofday(&tv1, &tz);
> + vdso_gettimeofday(&tv2, &tz);
> +
> + test_msg("gettimeofday: tv_sec %li vdso_gettimeofday: tv_sec %li\n",
> + tv1.tv_sec, tv2.tv_sec);
> +
> + if (abs(tv1.tv_sec - tv2.tv_sec) > TIME_DELTA_SEC) {
> + err("Delta is too big");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int vdso_time_handler(void *func)
> +{
> + __vdso_time_t *vdso_time = func;
> + time_t t1, t2;
> +
> + t1 = time(NULL);
> + t2 = vdso_time(NULL);
> +
> + test_msg("time: %li vdso_time: %li\n", (long)t1, (long)t1);
> +
> + if (abs(t1 - t2) > TIME_DELTA_SEC) {
> + err("Delta is too big");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + typedef int (handler_t)(void *func);
> +
> + struct vdso_symtable symtable;
> + size_t i;
> +
> + handler_t *handlers[VDSO_SYMBOL_MAX] = {
> + [VDSO_SYMBOL_CLOCK_GETTIME] = vdso_clock_gettime_handler,
> + [VDSO_SYMBOL_GETCPU] = vdso_getcpu_handler,
> + [VDSO_SYMBOL_GETTIMEOFDAY] = vdso_gettimeofday_handler,
> + [VDSO_SYMBOL_TIME] = vdso_time_handler,
> + };
> +
> + test_init(argc, argv);
> +
> + if (vdso_fill_self_symtable(&symtable)) {
> + err("Faied to parse vdso\n");
> + return -1;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(symtable.symbols); i++) {
> + struct vdso_symbol *s = &symtable.symbols[i];
> + handler_t *func;
> +
> + if (vdso_symbol_empty(s) || i > ARRAY_SIZE(handlers))
> + continue;
> + func = handlers[i];
> +
> + if (func((void *)(s->offset + symtable.vma_start))) {
> + err("Handler error");
> + return -1;
> + }
> + }
> +
> + test_daemon();
> + test_waitsig();
> +
> + /*
> + * After restore the vDSO must remain in old place.
> + */
> + for (i = 0; i < ARRAY_SIZE(symtable.symbols); i++) {
> + struct vdso_symbol *s = &symtable.symbols[i];
> + handler_t *func;
> +
> + if (vdso_symbol_empty(s) || i > ARRAY_SIZE(handlers))
> + continue;
> + func = handlers[i];
> +
> + if (func((void *)(s->offset + symtable.vma_start))) {
> + fail("Handler error");
> + return -1;
> + }
> + }
> +
> + pass();
> +
> + return 0;
> +}
> --
> 1.9.3
>
More information about the CRIU
mailing list