[CRIU] [PATCH] zdtm: Update vdso00 test

Cyrill Gorcunov gorcunov at openvz.org
Thu Jun 19 09:10:54 PDT 2014


To make sure vDSO functions are usable after restore
we parse vdso area on startup and the call for helpers
found before and after the restore.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 test/zdtm/live/static/vdso00.c | 407 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 397 insertions(+), 10 deletions(-)

diff --git a/test/zdtm/live/static/vdso00.c b/test/zdtm/live/static/vdso00.c
index 8ac4ccad9526..9577bd6d6f73 100644
--- a/test/zdtm/live/static/vdso00.c
+++ b/test/zdtm/live/static/vdso00.c
@@ -1,32 +1,419 @@
-#include <stdio.h>
 #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 after restore\n";
 const char *test_author	= "Cyrill Gorcunov <gorcunov at openvz.org";
 
-int main(int argc, char *argv[])
+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)
 {
-	struct timeval tv;
+	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);
-	test_msg("%s pid %d\n", argv[0], getpid());
 
-	gettimeofday(&tv, &tz);
-	test_msg("%d time: %10li\n", getpid(), tv.tv_sec);
+	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();
 
-	/* this call will fail if vDSO is corrupted */
-	gettimeofday(&tv, &tz);
-	test_msg("%d time: %10li\n", getpid(), tv.tv_sec);
+	/*
+	 * 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();
 
-- 
1.9.3



More information about the CRIU mailing list