mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-08-01 06:20:55 +00:00
zdtm: Add vdso01 test case
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@openvz.org> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
parent
fac7befa6b
commit
3423e3492b
2 changed files with 422 additions and 0 deletions
|
|
@ -45,6 +45,7 @@ TST_NOFILE = \
|
|||
pthread00 \
|
||||
pthread01 \
|
||||
vdso00 \
|
||||
vdso01 \
|
||||
utsname \
|
||||
pstree \
|
||||
sockets01 \
|
||||
|
|
|
|||
421
test/zdtm/live/static/vdso01.c
Normal file
421
test/zdtm/live/static/vdso01.c
Normal file
|
|
@ -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@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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue