Commit 91c81e5f authored by Dmitry Safonov's avatar Dmitry Safonov Committed by Andrei Vagin

kdat: Add test for presence of vdso mapping API

Previously, arch_prctl(ARCH_MAP_VDSO_32) was only used by
CONFIG_COMPAT to map compatible vdso blob for 32-bit restoree.
But we can make it more generic:
Omitting mremap() for moving vdso to rt-vdso zone in restorer
and afterward on needed position in restoree.
Also omitting reading /proc/self/maps to find vdso/vvar
addresses (to park afterward in restorer).

TLDR; under this kdat feature we can get rid of a buch of mremap()'s
for each restoree and from parsing /proc/self/maps in vdso_init_restore().

The API is present from v4.9 kernel.
Signed-off-by: 's avatarDmitry Safonov <dsafonov@virtuozzo.com>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent 977e5138
......@@ -53,6 +53,7 @@
#define kdat_compatible_cr() 0
#define kdat_can_map_vdso() 0
int restore_gpregs(struct rt_sigframe *f, UserAarch64RegsEntry *r);
int restore_nonsigframe_gpregs(UserAarch64RegsEntry *r);
......
......@@ -54,6 +54,7 @@
#define kdat_compatible_cr() 0
#define kdat_can_map_vdso() 0
int restore_gpregs(struct rt_sigframe *f, UserArmRegsEntry *r);
int restore_nonsigframe_gpregs(UserArmRegsEntry *r);
......
......@@ -49,6 +49,7 @@
: "memory","0","3","4","5","6","7","14","15")
#define kdat_compatible_cr() 0
#define kdat_can_map_vdso() 0
int restore_gpregs(struct rt_sigframe *f, UserPpc64RegsEntry *r);
int restore_nonsigframe_gpregs(UserPpc64RegsEntry *r);
......
......@@ -30,33 +30,48 @@
#include "images/core.pb-c.h"
#include "images/creds.pb-c.h"
#ifdef CONFIG_COMPAT
static int has_arch_map_vdso(void)
int kdat_can_map_vdso(void)
{
unsigned long auxval;
int ret;
errno = 0;
auxval = getauxval(AT_SYSINFO_EHDR);
if (!auxval) {
if (errno == ENOENT) { /* No vDSO - OK */
pr_warn("No SYSINFO_EHDR - no vDSO\n");
return 1;
} else { /* That can't happen, according to man */
pr_err("Failed to get auxval: errno %d\n", errno);
return -1;
}
}
pid_t child;
int stat;
/*
* Mapping vDSO while have not unmap it yet:
* this is restricted by API if ARCH_MAP_VDSO_* is supported.
* Running under fork so if vdso_64 is disabled - don't create
* it for criu accidentally.
*/
ret = syscall(SYS_arch_prctl, ARCH_MAP_VDSO_32, 1);
if (ret == -1 && errno == EEXIST)
return 1;
return 0;
child = fork();
if (child < 0)
return -1;
if (child == 0) {
int ret;
ret = syscall(SYS_arch_prctl, ARCH_MAP_VDSO_32, 0);
if (ret == 0)
exit(1);
/*
* Mapping vDSO while have not unmap it yet:
* this is restricted by API if ARCH_MAP_VDSO_* is supported.
*/
if (ret == -1 && errno == EEXIST)
exit(1);
exit(0);
}
if (waitpid(child, &stat, 0) != child) {
pr_err("Failed to wait for arch_prctl() test");
kill(child, SIGKILL);
return -1;
}
if (!WIFEXITED(stat))
return -1;
return WEXITSTATUS(stat);
}
#ifdef CONFIG_COMPAT
void *mmap_ia32(void *addr, size_t len, int prot,
int flags, int fildes, off_t off)
{
......@@ -151,13 +166,15 @@ static int has_32bit_mmap_bug(void)
int kdat_compatible_cr(void)
{
if (!has_arch_map_vdso())
if (!kdat.can_map_vdso)
return 0;
if (has_32bit_mmap_bug())
return 0;
return 1;
}
#else
#else /* !CONFIG_COMPAT */
int kdat_compatible_cr(void)
{
return 0;
......
......@@ -77,6 +77,7 @@ static inline int set_compat_robust_list(uint32_t head_ptr, uint32_t len)
#endif
extern int kdat_compatible_cr(void);
extern int kdat_can_map_vdso(void);
static inline void
__setup_sas_compat(struct ucontext_ia32* uc, ThreadSasEntry *sas)
......
......@@ -1054,6 +1054,14 @@ static int check_uffd_noncoop(void)
return 0;
}
static int check_can_map_vdso(void)
{
if (kdat_can_map_vdso() == 1)
return 0;
pr_warn("Do not have API to map vDSO - will use mremap() to restore vDSO\n");
return -1;
}
static int (*chk_feature)(void);
/*
......@@ -1157,6 +1165,7 @@ int cr_check(void)
ret |= check_tcp_halt_closed();
ret |= check_userns();
ret |= check_loginuid();
ret |= check_can_map_vdso();
}
/*
......@@ -1208,6 +1217,7 @@ static struct feature_list feature_list[] = {
{ "compat_cr", check_compat_cr },
{ "uffd", check_uffd },
{ "uffd-noncoop", check_uffd_noncoop },
{ "can_map_vdso", check_can_map_vdso},
{ NULL, NULL },
};
......
......@@ -55,6 +55,7 @@ struct kerndat_s {
bool has_uffd;
unsigned long uffd_features;
bool has_thp_disable;
bool can_map_vdso;
#ifdef CONFIG_VDSO
struct vdso_symtable vdso_sym;
#ifdef CONFIG_COMPAT
......
......@@ -560,11 +560,16 @@ err:
static int kerndat_compat_restore(void)
{
int ret = kdat_compatible_cr();
int ret;
if (ret < 0) /* failure */
ret = kdat_can_map_vdso();
if (ret < 0)
return ret;
kdat.compat_cr = !!ret;
kdat.can_map_vdso = !!ret;
/* depends on kdat.can_map_vdso result */
kdat.compat_cr = kdat_compatible_cr();
return 0;
}
......
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