Commit 0ae67a77 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Pavel Emelyanov

vdso: Escape double dumping of rt-vdso if proxy present

In case if we have created vdso proxy the rt-vdso should
not be dumped because it will be re-created on next restore
anyway. Thus with help of parasite service routine find
the rt-vdso and tear it off from VMAs list.
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 528106a5
......@@ -61,6 +61,7 @@
#include "kerndat.h"
#include "stats.h"
#include "mem.h"
#include "vdso.h"
#include "page-pipe.h"
#include "vdso.h"
......@@ -1484,6 +1485,12 @@ static int dump_one_task(struct pstree_item *item)
}
}
ret = parasite_fixup_vdso(parasite_ctl, pid, &vmas);
if (ret) {
pr_err("Can't fixup vdso VMAs (pid: %d)\n", pid);
goto err_cure_fdset;
}
ret = parasite_dump_misc_seized(parasite_ctl, &misc);
if (ret) {
pr_err("Can't dump misc (pid: %d)\n", pid);
......
......@@ -84,4 +84,7 @@ int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
extern int __parasite_execute(struct parasite_ctl *ctl, pid_t pid, user_regs_struct_t *regs);
extern bool arch_can_dump_task(pid_t pid);
extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
struct vm_area_list *vma_area_list);
#endif /* __CR_PARASITE_SYSCALL_H__ */
......@@ -35,6 +35,7 @@ enum {
PARASITE_CMD_DRAIN_FDS,
PARASITE_CMD_GET_PROC_FD,
PARASITE_CMD_DUMP_TTY,
PARASITE_CMD_CHECK_VDSO_MARK,
PARASITE_CMD_MAX,
};
......@@ -60,6 +61,13 @@ struct parasite_vma_entry
int prot;
};
struct parasite_vdso_vma_entry {
unsigned long start;
unsigned long len;
unsigned long proxy_addr;
int is_marked;
};
struct parasite_dump_pages_args {
unsigned int nr_vmas;
unsigned int add_prot;
......
......@@ -99,6 +99,13 @@ struct vdso_mark {
#define INIT_VDSO_MARK(m) \
*(m) = (struct vdso_mark)VDSO_MARK_INIT
static inline bool is_vdso_mark(void *addr)
{
struct vdso_mark *m = addr;
return m->signature == VDSO_MARK_SIGNATURE &&
m->proxy_addr != VDSO_BAD_ADDR;
}
extern struct vdso_symtable vdso_sym_rt;
extern u64 vdso_pfn;
......
......@@ -20,9 +20,11 @@
#include "parasite.h"
#include "crtools.h"
#include "namespaces.h"
#include "kerndat.h"
#include "pstree.h"
#include "net.h"
#include "mem.h"
#include "vdso.h"
#include <string.h>
#include <stdlib.h>
......@@ -511,6 +513,125 @@ err:
return ret;
}
/*
* Find out proxy vdso vma and drop it from the list. Also
* fix vdso status on vmas if wrong status found.
*/
int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
struct vm_area_list *vma_area_list)
{
unsigned long proxy_addr = VDSO_BAD_ADDR;
struct parasite_vdso_vma_entry *args;
struct vma_area *marked = NULL;
struct vma_area *vma;
int fd, ret = -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->vma.prot & VDSO_PROT) != VDSO_PROT)
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->vma.start;
args->len = vma_area_len(vma);
if (parasite_execute(PARASITE_CMD_CHECK_VDSO_MARK, ctl)) {
pr_err("vdso: Parasite failed to poke for mark\n");
ret = -1;
goto err;
}
off = (vma->vma.start / PAGE_SIZE) * sizeof(u64);
if (lseek(fd, off, SEEK_SET) != off) {
pr_perror("Failed to seek address %lx\n", vma->vma.start);
ret = -1;
goto err;
}
ret = read(fd, &pfn, sizeof(pfn));
if (ret < 0 || ret != sizeof(pfn)) {
pr_perror("Can't read pme for pid %d", pid);
ret = -1;
goto err;
}
pfn = PME_PFRAME(pfn);
BUG_ON(!pfn);
/*
* Defer handling marked vdso.
*/
if (unlikely(args->is_marked)) {
BUG_ON(args->proxy_addr == VDSO_BAD_ADDR);
BUG_ON(marked);
marked = vma;
proxy_addr = args->proxy_addr;
continue;
}
/*
* Set proper VMA statuses.
*/
if (pfn == vdso_pfn) {
if (!vma_area_is(vma, VMA_AREA_VDSO)) {
pr_debug("vdso: Restore status by pfn at %lx\n",
(long)vma->vma.start);
vma->vma.status |= VMA_AREA_VDSO;
}
} else {
if (vma_area_is(vma, VMA_AREA_VDSO)) {
pr_debug("vdso: Drop mishinted status at %lx\n",
(long)vma->vma.start);
vma->vma.status &= ~VMA_AREA_VDSO;
}
}
}
/*
* There is marked vdso, it means such vdso is autogenerated
* and must be dropped from vma list.
*/
if (marked) {
pr_debug("vdso: Found marked at %lx (proxy at %lx)\n",
(long)marked->vma.start, (long)proxy_addr);
/*
* Don't forget to restore the proxy vdso status, since
* it's being not recognized by the kernel as vdso.
*/
list_for_each_entry(vma, &vma_area_list->h, list) {
if (vma->vma.start == proxy_addr) {
vma->vma.status |= VMA_AREA_REGULAR | VMA_AREA_VDSO;
pr_debug("vdso: Restore proxy status at %lx\n",
(long)vma->vma.start);
break;
}
}
pr_debug("vdso: Droppping marked vdso at %lx\n",
(long)vma->vma.start);
list_del(&marked->list);
xfree(marked);
}
ret = 0;
err:
close(fd);
return ret;
}
int parasite_get_proc_fd_seized(struct parasite_ctl *ctl)
{
int ret = -1, fd;
......
......@@ -8,6 +8,7 @@
#include "syscall.h"
#include "parasite.h"
#include "vdso.h"
#include "log.h"
#include <string.h>
......@@ -415,6 +416,21 @@ static int parasite_cfg_log(struct parasite_log_args *args)
return ret;
}
static int parasite_check_vdso_mark(struct parasite_vdso_vma_entry *args)
{
struct vdso_mark *m = (void *)args->start;
if (is_vdso_mark(m)) {
args->is_marked = 1;
args->proxy_addr = m->proxy_addr;
} else {
args->is_marked = 0;
args->proxy_addr = VDSO_BAD_ADDR;
}
return 0;
}
static int fini(void)
{
int ret;
......@@ -463,6 +479,8 @@ int __used parasite_service(unsigned int cmd, void *args)
return parasite_get_proc_fd();
case PARASITE_CMD_DUMP_TTY:
return parasite_dump_tty(args);
case PARASITE_CMD_CHECK_VDSO_MARK:
return parasite_check_vdso_mark(args);
}
pr_err("Unknown command to parasite: %d\n", cmd);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment