[RFC 2/6] x86,vdso: Use special mapping tracking for the vdso

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]