[RFC 2/6] x86,vdso: Use special mapping tracking for the vdso
From: Andy Lutomirski
Date: Wed Oct 29 2014 - 20:42:36 EST
This should give full support for mremap on the vdso except for
sysenter return. It will also enable future vvar twiddling on
already-started processes.
Signed-off-by: Andy Lutomirski <luto@xxxxxxxxxxxxxx>
---
arch/x86/ia32/ia32_signal.c | 11 ++++-------
arch/x86/include/asm/elf.h | 26 ++++++++-----------------
arch/x86/include/asm/mmu.h | 4 +++-
arch/x86/include/asm/vdso.h | 16 +++++++++++++++
arch/x86/kernel/signal.c | 9 +++------
arch/x86/vdso/vma.c | 47 ++++++++++++++++++++++++++++++++++++++-------
6 files changed, 74 insertions(+), 39 deletions(-)
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index f9e181aaba97..3b335c674059 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -381,11 +381,8 @@ int ia32_setup_frame(int sig, struct ksignal *ksig,
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
restorer = ksig->ka.sa.sa_restorer;
} else {
- /* Return stub is in 32bit vsyscall page */
- if (current->mm->context.vdso)
- restorer = current->mm->context.vdso +
- selected_vdso32->sym___kernel_sigreturn;
- else
+ restorer = VDSO_SYM_ADDR(current->mm, __kernel_sigreturn);
+ if (!restorer)
restorer = &frame->retcode;
}
@@ -462,8 +459,8 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = ksig->ka.sa.sa_restorer;
else
- restorer = current->mm->context.vdso +
- selected_vdso32->sym___kernel_rt_sigreturn;
+ restorer = VDSO_SYM_ADDR(current->mm,
+ __kernel_rt_sigreturn);
put_user_ex(ptr_to_compat(restorer), &frame->pretcode);
/*
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 1a055c81d864..05df8f03faa5 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -276,7 +276,7 @@ struct task_struct;
#define ARCH_DLINFO_IA32 \
do { \
- if (vdso32_enabled) { \
+ if (current->mm->context.vdso_image) { \
NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \
NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \
} \
@@ -295,26 +295,19 @@ do { \
/* 1GB for 64bit, 8MB for 32bit */
#define STACK_RND_MASK (test_thread_flag(TIF_ADDR32) ? 0x7ff : 0x3fffff)
-#define ARCH_DLINFO \
+#define ARCH_DLINFO_X86_64 \
do { \
- if (vdso64_enabled) \
- NEW_AUX_ENT(AT_SYSINFO_EHDR, \
- (unsigned long __force)current->mm->context.vdso); \
+ if (current->mm->context.vdso_image) \
+ NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \
} while (0)
-/* As a historical oddity, the x32 and x86_64 vDSOs are controlled together. */
-#define ARCH_DLINFO_X32 \
-do { \
- if (vdso64_enabled) \
- NEW_AUX_ENT(AT_SYSINFO_EHDR, \
- (unsigned long __force)current->mm->context.vdso); \
-} while (0)
+#define ARCH_DLINFO ARCH_DLINFO_X86_64
#define AT_SYSINFO 32
#define COMPAT_ARCH_DLINFO \
if (test_thread_flag(TIF_X32)) \
- ARCH_DLINFO_X32; \
+ ARCH_DLINFO_X86_64; \
else \
ARCH_DLINFO_IA32
@@ -322,11 +315,8 @@ else \
#endif /* !CONFIG_X86_32 */
-#define VDSO_CURRENT_BASE ((unsigned long)current->mm->context.vdso)
-
-#define VDSO_ENTRY \
- ((unsigned long)current->mm->context.vdso + \
- selected_vdso32->sym___kernel_vsyscall)
+#define VDSO_CURRENT_BASE ((unsigned long)vdso_text_start(current->mm))
+#define VDSO_ENTRY ((unsigned long)VDSO_SYM_ADDR(current->mm, __kernel_vsyscall))
struct linux_binprm;
diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h
index 876e74e8eec7..bbba90ebd2c8 100644
--- a/arch/x86/include/asm/mmu.h
+++ b/arch/x86/include/asm/mmu.h
@@ -18,7 +18,9 @@ typedef struct {
#endif
struct mutex lock;
- void __user *vdso;
+
+ unsigned long vvar_vma_start;
+ const struct vdso_image *vdso_image;
} mm_context_t;
#ifdef CONFIG_SMP
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 8021bd28c0f1..3aa1f830c551 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -49,6 +49,22 @@ extern const struct vdso_image *selected_vdso32;
extern void __init init_vdso_image(const struct vdso_image *image);
+static inline void __user *vdso_text_start(const struct mm_struct *mm)
+{
+ if (!mm->context.vdso_image)
+ return NULL;
+
+ return (void __user *)ACCESS_ONCE(mm->context.vvar_vma_start) -
+ mm->context.vdso_image->sym_vvar_start;
+}
+
+#define VDSO_SYM_ADDR(mm, sym) ( \
+ (mm)->context.vdso_image ? \
+ vdso_text_start((mm)) + \
+ (mm)->context.vdso_image->sym_ ## sym \
+ : NULL \
+ )
+
#endif /* __ASSEMBLER__ */
#endif /* _ASM_X86_VDSO_H */
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 2851d63c1202..d8b21e37e292 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -297,10 +297,8 @@ __setup_frame(int sig, struct ksignal *ksig, sigset_t *set,
return -EFAULT;
}
- if (current->mm->context.vdso)
- restorer = current->mm->context.vdso +
- selected_vdso32->sym___kernel_sigreturn;
- else
+ restorer = VDSO_SYM_ADDR(current->mm, __kernel_sigreturn);
+ if (!restorer)
restorer = &frame->retcode;
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = ksig->ka.sa.sa_restorer;
@@ -362,8 +360,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
save_altstack_ex(&frame->uc.uc_stack, regs->sp);
/* Set up to return from userspace. */
- restorer = current->mm->context.vdso +
- selected_vdso32->sym___kernel_rt_sigreturn;
+ restorer = VDSO_SYM_ADDR(current->mm, __kernel_rt_sigreturn);
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = ksig->ka.sa.sa_restorer;
put_user_ex(restorer, &frame->pretcode);
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c
index 970463b566cf..7f99c2ed1a3e 100644
--- a/arch/x86/vdso/vma.c
+++ b/arch/x86/vdso/vma.c
@@ -89,6 +89,38 @@ static unsigned long vdso_addr(unsigned long start, unsigned len)
#endif
}
+static void vvar_start_set(struct vm_special_mapping *sm,
+ struct mm_struct *mm, unsigned long start_addr)
+{
+ if (start_addr >= TASK_SIZE_MAX - mm->context.vdso_image->size) {
+ /*
+ * We were just relocated out of bounds. Malicious
+ * user code can cause this by mremapping only the
+ * first page of a multi-page vdso.
+ *
+ * We can't actually fail here, but it's not safe to
+ * allow vdso symbols to resolve to potentially
+ * non-canonical addresses. Instead, just ignore
+ * the update.
+ */
+
+ return;
+ }
+
+ mm->context.vvar_vma_start = start_addr;
+
+ /*
+ * If we're here as a result of an mremap call, there are two
+ * major gotchas. First, if that call came from the vdso, we're
+ * about to crash (i.e. don't do that). Second, if we have more
+ * than one thread, this won't update the other threads.
+ */
+ if (mm->context.vdso_image->sym_VDSO32_SYSENTER_RETURN)
+ current_thread_info()->sysenter_return =
+ VDSO_SYM_ADDR(current->mm, VDSO32_SYSENTER_RETURN);
+
+}
+
static int map_vdso(const struct vdso_image *image, bool calculate_addr)
{
struct mm_struct *mm = current->mm;
@@ -99,6 +131,12 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
static struct vm_special_mapping vvar_mapping = {
.name = "[vvar]",
.pages = no_pages,
+
+ /*
+ * Tracking the vdso is roughly equivalent to tracking the
+ * vvar area, and the latter is slightly easier.
+ */
+ .start_addr_set = vvar_start_set,
};
if (calculate_addr) {
@@ -118,7 +156,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
}
text_start = addr - image->sym_vvar_start;
- current->mm->context.vdso = (void __user *)text_start;
+ current->mm->context.vdso_image = image;
/*
* MAYWRITE to allow gdb to COW and set breakpoints
@@ -171,7 +209,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
up_fail:
if (ret)
- current->mm->context.vdso = NULL;
+ current->mm->context.vdso_image = NULL;
up_write(&mm->mmap_sem);
return ret;
@@ -189,11 +227,6 @@ static int load_vdso32(void)
if (ret)
return ret;
- if (selected_vdso32->sym_VDSO32_SYSENTER_RETURN)
- current_thread_info()->sysenter_return =
- current->mm->context.vdso +
- selected_vdso32->sym_VDSO32_SYSENTER_RETURN;
-
return 0;
}
#endif
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/