mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 10:16:41 +00:00
Merge arch/*/vdso.c files in vdso.c. The merge has been done by copying arch/x86/vdso.c in vdso.c since it contains patcha67e9f7bb9("vdso: don't play with a function exit code"). The commitf9ae6d9dd4("Replace remaining hard-coded TASK_SIZE use") pushed in the aarch64 has been replicated since it should be cross platform. CC: Christopher Covington <cov@codeaurora.org> CC: Pavel Emelyanov <xemul@parallels.com> Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com> Acked-by: Cyrill Gorcunov <gorcunov@gmail.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
294 lines
7.1 KiB
C
294 lines
7.1 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <elf.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include "asm/types.h"
|
|
#include "asm/parasite-syscall.h"
|
|
|
|
#include "parasite-syscall.h"
|
|
#include "parasite.h"
|
|
#include "compiler.h"
|
|
#include "kerndat.h"
|
|
#include "vdso.h"
|
|
#include "util.h"
|
|
#include "log.h"
|
|
#include "mem.h"
|
|
#include "vma.h"
|
|
|
|
#ifdef LOG_PREFIX
|
|
# undef LOG_PREFIX
|
|
#endif
|
|
#define LOG_PREFIX "vdso: "
|
|
|
|
struct vdso_symtable vdso_sym_rt = VDSO_SYMTABLE_INIT;
|
|
u64 vdso_pfn = VDSO_BAD_PFN;
|
|
/*
|
|
* The VMAs list might have proxy vdso/vvar areas left
|
|
* from previous dump/restore cycle so we need to detect
|
|
* them and eliminated from the VMAs list, they will be
|
|
* generated again on restore if needed.
|
|
*/
|
|
int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
|
|
struct vm_area_list *vma_area_list)
|
|
{
|
|
unsigned long proxy_vdso_addr = VDSO_BAD_ADDR;
|
|
unsigned long proxy_vvar_addr = VVAR_BAD_ADDR;
|
|
struct vma_area *proxy_vdso_marked = NULL;
|
|
struct vma_area *proxy_vvar_marked = NULL;
|
|
struct parasite_vdso_vma_entry *args;
|
|
struct vma_area *vma;
|
|
int fd, ret, exit_code = -1;
|
|
off_t off;
|
|
u64 pfn;
|
|
|
|
args = parasite_args(ctl, struct parasite_vdso_vma_entry);
|
|
fd = open_proc(pid, "pagemap");
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
list_for_each_entry(vma, &vma_area_list->h, list) {
|
|
if (!vma_area_is(vma, VMA_AREA_REGULAR))
|
|
continue;
|
|
|
|
if (vma_area_is(vma, VMA_FILE_SHARED) ||
|
|
vma_area_is(vma, VMA_FILE_PRIVATE))
|
|
continue;
|
|
/*
|
|
* It might be possible VVAR area from marked
|
|
* vDSO zone, we need to detect it earlier than
|
|
* VDSO_PROT test because VVAR_PROT is a subset
|
|
* of it but don't yield continue here,
|
|
* sigh... what a mess.
|
|
*/
|
|
BUILD_BUG_ON(!(VDSO_PROT & VVAR_PROT));
|
|
|
|
if ((vma->e->prot & VVAR_PROT) == VVAR_PROT) {
|
|
if (proxy_vvar_addr != VVAR_BAD_ADDR &&
|
|
proxy_vvar_addr == vma->e->start) {
|
|
BUG_ON(proxy_vvar_marked);
|
|
proxy_vvar_marked = vma;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((vma->e->prot & VDSO_PROT) != VDSO_PROT)
|
|
continue;
|
|
|
|
if (vma->e->start > kdat.task_size)
|
|
continue;
|
|
|
|
if (vma->e->flags & MAP_GROWSDOWN)
|
|
continue;
|
|
|
|
/*
|
|
* I need to poke every potentially marked vma,
|
|
* otherwise if task never called for vdso functions
|
|
* page frame number won't be reported.
|
|
*/
|
|
args->start = vma->e->start;
|
|
args->len = vma_area_len(vma);
|
|
|
|
if (parasite_execute_daemon(PARASITE_CMD_CHECK_VDSO_MARK, ctl)) {
|
|
pr_err("vdso: Parasite failed to poke for mark\n");
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Defer handling marked vdso until we walked over
|
|
* all vmas and restore potentially remapped vDSO
|
|
* area status.
|
|
*/
|
|
if (unlikely(args->is_marked)) {
|
|
if (proxy_vdso_marked) {
|
|
pr_err("Ow! Second vdso mark detected!\n");
|
|
goto err;
|
|
}
|
|
proxy_vdso_marked = vma;
|
|
proxy_vdso_addr = args->proxy_vdso_addr;
|
|
proxy_vvar_addr = args->proxy_vvar_addr;
|
|
continue;
|
|
}
|
|
|
|
off = (vma->e->start / PAGE_SIZE) * sizeof(u64);
|
|
ret = pread(fd, &pfn, sizeof(pfn), off);
|
|
if (ret < 0 || ret != sizeof(pfn)) {
|
|
pr_perror("Can't read pme for pid %d", pid);
|
|
goto err;
|
|
}
|
|
|
|
pfn = PME_PFRAME(pfn);
|
|
if (!pfn) {
|
|
pr_err("Unexpected page fram number 0 for pid %d\n", pid);
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Setup proper VMA status. Note starting with 3.16
|
|
* the [vdso]/[vvar] marks are reported correctly
|
|
* even when they are remapped into a new place,
|
|
* but only since that particular version of the
|
|
* kernel!
|
|
*/
|
|
if (pfn == vdso_pfn) {
|
|
if (!vma_area_is(vma, VMA_AREA_VDSO)) {
|
|
pr_debug("vdso: Restore vDSO status by pfn at %lx\n",
|
|
(long)vma->e->start);
|
|
vma->e->status |= VMA_AREA_VDSO;
|
|
}
|
|
} else {
|
|
if (unlikely(vma_area_is(vma, VMA_AREA_VDSO))) {
|
|
pr_debug("vdso: Drop mishinted vDSO status at %lx\n",
|
|
(long)vma->e->start);
|
|
vma->e->status &= ~VMA_AREA_VDSO;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* There is marked vdso, it means such vdso is autogenerated
|
|
* and must be dropped from vma list.
|
|
*/
|
|
if (proxy_vdso_marked) {
|
|
pr_debug("vdso: Found marked at %lx (proxy vDSO at %lx VVAR at %lx)\n",
|
|
(long)proxy_vdso_marked->e->start,
|
|
(long)proxy_vdso_addr, (long)proxy_vvar_addr);
|
|
|
|
/*
|
|
* Don't forget to restore the proxy vdso/vvar status, since
|
|
* it's unknown to the kernel.
|
|
*/
|
|
list_for_each_entry(vma, &vma_area_list->h, list) {
|
|
if (vma->e->start == proxy_vdso_addr) {
|
|
vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VDSO;
|
|
pr_debug("vdso: Restore proxy vDSO status at %lx\n",
|
|
(long)vma->e->start);
|
|
} else if (vma->e->start == proxy_vvar_addr) {
|
|
vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VVAR;
|
|
pr_debug("vdso: Restore proxy VVAR status at %lx\n",
|
|
(long)vma->e->start);
|
|
}
|
|
}
|
|
|
|
pr_debug("vdso: Droppping marked vdso at %lx\n",
|
|
(long)proxy_vdso_marked->e->start);
|
|
list_del(&proxy_vdso_marked->list);
|
|
xfree(proxy_vdso_marked);
|
|
vma_area_list->nr--;
|
|
|
|
if (proxy_vvar_marked) {
|
|
pr_debug("vdso: Droppping marked vvar at %lx\n",
|
|
(long)proxy_vvar_marked->e->start);
|
|
list_del(&proxy_vvar_marked->list);
|
|
xfree(proxy_vvar_marked);
|
|
vma_area_list->nr--;
|
|
}
|
|
}
|
|
exit_code = 0;
|
|
err:
|
|
close(fd);
|
|
return exit_code;
|
|
}
|
|
|
|
static int vdso_fill_self_symtable(struct vdso_symtable *s)
|
|
{
|
|
char buf[512];
|
|
int ret, exit_code = -1;
|
|
FILE *maps;
|
|
|
|
*s = (struct vdso_symtable)VDSO_SYMTABLE_INIT;
|
|
|
|
maps = fopen_proc(PROC_SELF, "maps");
|
|
if (!maps) {
|
|
pr_perror("Can't open self-vma");
|
|
return -1;
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), maps)) {
|
|
unsigned long start, end;
|
|
char *has_vdso, *has_vvar;
|
|
|
|
has_vdso = strstr(buf, "[vdso]");
|
|
if (!has_vdso)
|
|
has_vvar = strstr(buf, "[vvar]");
|
|
else
|
|
has_vvar = NULL;
|
|
|
|
if (!has_vdso && !has_vvar)
|
|
continue;
|
|
|
|
ret = sscanf(buf, "%lx-%lx", &start, &end);
|
|
if (ret != 2) {
|
|
pr_err("Can't find vDSO/VVAR bounds\n");
|
|
goto err;
|
|
}
|
|
|
|
if (has_vdso) {
|
|
if (s->vma_start != VDSO_BAD_ADDR) {
|
|
pr_err("Got second vDSO entry\n");
|
|
goto err;
|
|
}
|
|
s->vma_start = start;
|
|
s->vma_end = end;
|
|
|
|
ret = vdso_fill_symtable((void *)start, end - start, s);
|
|
if (ret)
|
|
goto err;
|
|
} else {
|
|
if (s->vvar_start != VVAR_BAD_ADDR) {
|
|
pr_err("Got second VVAR entry\n");
|
|
goto err;
|
|
}
|
|
s->vvar_start = start;
|
|
s->vvar_end = end;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Validate its structure -- for new vDSO format the
|
|
* structure must be like
|
|
*
|
|
* 7fff1f5fd000-7fff1f5fe000 r-xp 00000000 00:00 0 [vdso]
|
|
* 7fff1f5fe000-7fff1f600000 r--p 00000000 00:00 0 [vvar]
|
|
*
|
|
* The areas may be in reverse order.
|
|
*
|
|
* 7fffc3502000-7fffc3504000 r--p 00000000 00:00 0 [vvar]
|
|
* 7fffc3504000-7fffc3506000 r-xp 00000000 00:00 0 [vdso]
|
|
*
|
|
*/
|
|
if (s->vma_start != VDSO_BAD_ADDR) {
|
|
if (s->vvar_start != VVAR_BAD_ADDR) {
|
|
if (s->vma_end != s->vvar_start &&
|
|
s->vvar_end != s->vma_start) {
|
|
pr_err("Unexpected rt vDSO area bounds\n");
|
|
goto err;
|
|
}
|
|
}
|
|
} else {
|
|
pr_err("Can't find rt vDSO\n");
|
|
goto err;
|
|
}
|
|
|
|
pr_debug("rt [vdso] %lx-%lx [vvar] %lx-%lx\n",
|
|
s->vma_start, s->vma_end,
|
|
s->vvar_start, s->vvar_end);
|
|
|
|
exit_code = 0;
|
|
err:
|
|
fclose(maps);
|
|
return exit_code;
|
|
}
|
|
|
|
int vdso_init(void)
|
|
{
|
|
if (vdso_fill_self_symtable(&vdso_sym_rt))
|
|
return -1;
|
|
return vaddr_to_pfn(vdso_sym_rt.vma_start, &vdso_pfn);
|
|
}
|