Commit ff28da95 authored by Dmitry Safonov's avatar Dmitry Safonov Committed by Andrei Vagin

vdso/kdat: Add test for preserving "[vdso]" hint after mremap()

If it does preserve, we can omit checking pagemap for dumpee or
filling symtable in parasite.
Signed-off-by: 's avatarDmitry Safonov <dsafonov@virtuozzo.com>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent b8f3fca4
...@@ -56,6 +56,7 @@ struct kerndat_s { ...@@ -56,6 +56,7 @@ struct kerndat_s {
unsigned long uffd_features; unsigned long uffd_features;
bool has_thp_disable; bool has_thp_disable;
bool can_map_vdso; bool can_map_vdso;
bool vdso_hint_reliable;
#ifdef CONFIG_VDSO #ifdef CONFIG_VDSO
struct vdso_symtable vdso_sym; struct vdso_symtable vdso_sym;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
......
...@@ -16,6 +16,7 @@ extern struct vdso_maps vdso_maps_compat; ...@@ -16,6 +16,7 @@ extern struct vdso_maps vdso_maps_compat;
extern int vdso_init_dump(void); extern int vdso_init_dump(void);
extern int vdso_init_restore(void); extern int vdso_init_restore(void);
extern int kerndat_vdso_fill_symtable(void); extern int kerndat_vdso_fill_symtable(void);
extern int kerndat_vdso_preserves_hint(void);
extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid, extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
struct vm_area_list *vma_area_list); struct vm_area_list *vma_area_list);
...@@ -30,6 +31,7 @@ extern void compat_vdso_helper(struct vdso_maps *native, int pipe_fd, ...@@ -30,6 +31,7 @@ extern void compat_vdso_helper(struct vdso_maps *native, int pipe_fd,
#define vdso_init_dump() (0) #define vdso_init_dump() (0)
#define vdso_init_restore() (0) #define vdso_init_restore() (0)
#define kerndat_vdso_fill_symtable() (0) #define kerndat_vdso_fill_symtable() (0)
#define kerndat_vdso_preserves_hint() (0)
#define parasite_fixup_vdso(ctl, pid, vma_area_list) (0) #define parasite_fixup_vdso(ctl, pid, vma_area_list) (0)
#endif /* CONFIG_VDSO */ #endif /* CONFIG_VDSO */
......
...@@ -870,6 +870,9 @@ int kerndat_init(void) ...@@ -870,6 +870,9 @@ int kerndat_init(void)
/* Needs kdat.compat_cr filled before */ /* Needs kdat.compat_cr filled before */
if (!ret) if (!ret)
ret = kerndat_vdso_fill_symtable(); ret = kerndat_vdso_fill_symtable();
/* Depends on kerndat_vdso_fill_symtable() */
if (!ret)
ret = kerndat_vdso_preserves_hint();
kerndat_lsm(); kerndat_lsm();
kerndat_mmap_min_addr(); kerndat_mmap_min_addr();
......
...@@ -621,3 +621,78 @@ int kerndat_vdso_fill_symtable(void) ...@@ -621,3 +621,78 @@ int kerndat_vdso_fill_symtable(void)
return 0; return 0;
} }
/*
* On x86 pre-v3.16 kernels can lose "[vdso]" hint
* in /proc/.../maps file after mremap()'ing vdso vma.
* Depends on kerndat_vdso_fill_symtable() - assuming that
* vdso_maps and vdso_maps_compat are filled.
*/
int kerndat_vdso_preserves_hint(void)
{
struct vdso_maps vdso_maps_after;
int status, ret = -1;
pid_t child;
kdat.vdso_hint_reliable = 0;
if (vdso_maps.vdso_start == VDSO_BAD_ADDR)
return 0;
child = fork();
if (child < 0) {
pr_perror("fork() failed");
return -1;
}
if (child == 0) {
unsigned long vdso_addr = vdso_maps.vdso_start;
unsigned long vdso_size = vdso_maps.sym.vdso_size;
void *new_addr;
new_addr = mmap(0, vdso_size, PROT_NONE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (new_addr == MAP_FAILED)
exit(1);
child = getpid();
new_addr = (void *)syscall(SYS_mremap, vdso_addr, vdso_size,
vdso_size, MREMAP_MAYMOVE | MREMAP_FIXED, new_addr);
if (new_addr == MAP_FAILED)
syscall(SYS_exit, 2);
syscall(SYS_kill, child, SIGSTOP);
syscall(SYS_exit, 3);
}
waitpid(child, &status, WUNTRACED);
if (WIFEXITED(status)) {
int ret = WEXITSTATUS(status);
pr_err("Child unexpectedly exited with %d\n", ret);
goto out;
} else if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
pr_err("Child unexpectedly signaled with %d: %s\n",
sig, strsignal(sig));
goto out;
} else if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) {
pr_err("Child is unstoppable or was stopped by other means\n");
goto out_kill;
}
if (vdso_parse_maps(child, &vdso_maps_after)) {
pr_err("Failed parsing maps for child helper\n");
goto out_kill;
}
if (vdso_maps_after.vdso_start != VDSO_BAD_ADDR)
kdat.vdso_hint_reliable = 1;
ret = 0;
out_kill:
kill(child, SIGKILL);
waitpid(child, &status, 0);
out:
return ret;
}
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