[CRIU] [PATCH] zdtm: Add vdso01 test case
Cyrill Gorcunov
gorcunov at openvz.org
Mon Jun 23 06:00:28 PDT 2014
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.
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