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