Commit 6eaa4e92 authored by Laurent Dufour's avatar Laurent Dufour Committed by Pavel Emelyanov

vdso: merge per arch vdso.c file in a common one

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 patch a67e9f7b ("vdso: don't play with a function exit code").

The commit f9ae6d9d ("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: 's avatarLaurent Dufour <ldufour@linux.vnet.ibm.com>
Acked-by: 's avatarCyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 94807751
......@@ -66,7 +66,7 @@ obj-y += string.o
obj-y += sigframe.o
obj-y += lsm.o
ifeq ($(VDSO),y)
obj-y += $(ARCH_DIR)/vdso.o
obj-y += vdso.o
endif
obj-y += cr-service.o
obj-y += sd-daemon.o
......
#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 = -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->prot != VDSO_PROT) {
pr_debug("Dropping %lx using extra protection test\n",
vma->e->start);
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");
ret = -1;
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");
ret = -1;
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);
ret = -1;
goto err;
}
pfn = PME_PFRAME(pfn);
if (!pfn) {
pr_err("Unexpected page fram number 0 for pid %d\n", pid);
ret = -1;
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--;
}
}
ret = 0;
err:
close(fd);
return ret;
}
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(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) {
ret = -1;
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");
ret = -1;
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");
ret = -1;
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]
*
*/
ret = 0;
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) {
ret = -1;
pr_err("Unexpected rt vDSO area bounds\n");
goto err;
}
}
} else {
ret = -1;
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);
err:
fclose(maps);
return ret;
}
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);
}
#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 = -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->prot != VDSO_PROT) {
pr_debug("Dropping %lx using extra protection test\n",
vma->e->start);
continue;
}
if (vma->e->start > 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");
ret = -1;
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");
ret = -1;
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);
ret = -1;
goto err;
}
pfn = PME_PFRAME(pfn);
if (!pfn) {
pr_err("Unexpected page fram number 0 for pid %d\n", pid);
ret = -1;
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--;
}
}
ret = 0;
err:
close(fd);
return ret;
}
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(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) {
ret = -1;
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");
ret = -1;
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");
ret = -1;
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]
*
*/
ret = 0;
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) {
ret = -1;
pr_err("Unexpected rt vDSO area bounds\n");
goto err;
}
}
} else {
ret = -1;
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);
err:
fclose(maps);
return ret;
}
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);
}
......@@ -81,7 +81,7 @@ int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
if ((vma->e->prot & VDSO_PROT) != VDSO_PROT)
continue;
if (vma->e->start > TASK_SIZE)
if (vma->e->start > kdat.task_size)
continue;
if (vma->e->flags & MAP_GROWSDOWN)
......
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