Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1381720 > unrolled thread

[PATCHv5 2/3] x86/vdso: add mremap hook to vm_special_mapping

Started byDmitry Safonov <dsafonov@virtuozzo.com>
First post2016-04-18 15:50 +0200
Last post2016-04-24 01:20 +0200
Articles 7 — 3 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  [PATCHv5 2/3] x86/vdso: add mremap hook to vm_special_mapping Dmitry Safonov <dsafonov@virtuozzo.com> - 2016-04-18 15:50 +0200
    Re: [PATCHv5 2/3] x86/vdso: add mremap hook to vm_special_mapping kbuild test robot <lkp@intel.com> - 2016-04-18 16:10 +0200
    [PATCHv6 2/3] x86/vdso: add mremap hook to vm_special_mapping Dmitry Safonov <dsafonov@virtuozzo.com> - 2016-04-18 16:20 +0200
    [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping Dmitry Safonov <dsafonov@virtuozzo.com> - 2016-04-18 16:30 +0200
      Re: [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping Dmitry Safonov <dsafonov@virtuozzo.com> - 2016-04-20 18:40 +0200
      Re: [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping Andy Lutomirski <luto@amacapital.net> - 2016-04-21 22:00 +0200
    Re: [PATCHv5 2/3] x86/vdso: add mremap hook to vm_special_mapping kbuild test robot <lkp@intel.com> - 2016-04-24 01:20 +0200

#1381720 — [PATCHv5 2/3] x86/vdso: add mremap hook to vm_special_mapping

FromDmitry Safonov <dsafonov@virtuozzo.com>
Date2016-04-18 15:50 +0200
Subject[PATCHv5 2/3] x86/vdso: add mremap hook to vm_special_mapping
Message-ID<rphIT-6ug-31@gated-at.bofh.it>
Add possibility for userspace 32-bit applications to move
vdso mapping. Previously, when userspace app called
mremap for vdso, in return path it would land on previous
address of vdso page, resulting in segmentation violation.
Now it lands fine and returns to userspace with remapped vdso.
This will also fix context.vdso pointer for 64-bit, which does not
affect the user of vdso after mremap by now, but this may change.

As suggested by Andy, return EINVAL for mremap that splits vdso image.

Renamed and moved text_mapping structure declaration inside
map_vdso, as it used only there and now it complement
vvar_mapping variable.

There is still problem for remapping vdso in glibc applications:
linker relocates addresses for syscalls on vdso page, so
you need to relink with the new addresses. Or the next syscall
through glibc may fail:
  Program received signal SIGSEGV, Segmentation fault.
  #0  0xf7fd9b80 in __kernel_vsyscall ()
  #1  0xf7ec8238 in _exit () from /usr/lib32/libc.so.6

Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
---
v5: as Andy suggested, add a check that new_vma->vm_mm and current->mm are
    the same, also check not only in_ia32_syscall() but image == &vdso_image_32
v4: drop __maybe_unused & use image from mm->context instead vdso_image_32
v3: as Andy suggested, return EINVAL in case of splitting vdso blob on mremap;
    used is_ia32_task instead of ifdefs 
v2: added __maybe_unused for pt_regs in vdso_mremap

 arch/x86/entry/vdso/vma.c | 39 ++++++++++++++++++++++++++++++++++-----
 include/linux/mm_types.h  |  3 +++
 mm/mmap.c                 | 10 ++++++++++
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 10f704584922..ba133a9ab4f5 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -12,6 +12,7 @@
 #include <linux/random.h>
 #include <linux/elf.h>
 #include <linux/cpu.h>
+#include <linux/ptrace.h>
 #include <asm/pvclock.h>
 #include <asm/vgtod.h>
 #include <asm/proto.h>
@@ -98,10 +99,32 @@ static int vdso_fault(const struct vm_special_mapping *sm,
 	return 0;
 }
 
-static const struct vm_special_mapping text_mapping = {
-	.name = "[vdso]",
-	.fault = vdso_fault,
-};
+static int vdso_mremap(const struct vm_special_mapping *sm,
+		      struct vm_area_struct *new_vma)
+{
+	unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
+	const struct vdso_image *image = current->mm->context.vdso_image;
+
+	if (image->size != new_size)
+		return -EINVAL;
+
+	if (current->mm != new_vma->vm_mm)
+		return -EFAULT;
+
+	if (in_ia32_syscall() && image == &vdso_image_32) {
+		struct pt_regs *regs = current_pt_regs();
+		unsigned long vdso_land = image->sym_int80_landing_pad;
+		unsigned long old_land_addr = vdso_land +
+			(unsigned long)current->mm->context.vdso;
+
+		/* Fixing userspace landing - look at do_fast_syscall_32 */
+		if (regs->ip == old_land_addr)
+			regs->ip = new_vma->vm_start + vdso_land;
+	}
+	current->mm->context.vdso = (void __user *)new_vma->vm_start;
+
+	return 0;
+}
 
 static int vvar_fault(const struct vm_special_mapping *sm,
 		      struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -162,6 +185,12 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
 	struct vm_area_struct *vma;
 	unsigned long addr, text_start;
 	int ret = 0;
+
+	static const struct vm_special_mapping vdso_mapping = {
+		.name = "[vdso]",
+		.fault = vdso_fault,
+		.mremap = vdso_mremap,
+	};
 	static const struct vm_special_mapping vvar_mapping = {
 		.name = "[vvar]",
 		.fault = vvar_fault,
@@ -195,7 +224,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
 				       image->size,
 				       VM_READ|VM_EXEC|
 				       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
-				       &text_mapping);
+				       &vdso_mapping);
 
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index c2d75b4fa86c..4d16ab9287af 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -586,6 +586,9 @@ struct vm_special_mapping {
 	int (*fault)(const struct vm_special_mapping *sm,
 		     struct vm_area_struct *vma,
 		     struct vm_fault *vmf);
+
+	int (*mremap)(const struct vm_special_mapping *sm,
+		     struct vm_area_struct *new_vma);
 };
 
 enum tlb_flush_reason {
diff --git a/mm/mmap.c b/mm/mmap.c
index bd2e1a533bc1..ba71658dd1a1 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2930,9 +2930,19 @@ static const char *special_mapping_name(struct vm_area_struct *vma)
 	return ((struct vm_special_mapping *)vma->vm_private_data)->name;
 }
 
+static int special_mapping_mremap(struct vm_area_struct *new_vma)
+{
+	struct vm_special_mapping *sm = new_vma->vm_private_data;
+
+	if (sm->mremap)
+		return sm->mremap(sm, new_vma);
+	return 0;
+}
+
 static const struct vm_operations_struct special_mapping_vmops = {
 	.close = special_mapping_close,
 	.fault = special_mapping_fault,
+	.mremap = special_mapping_mremap,
 	.name = special_mapping_name,
 };
 
-- 
2.8.0

[toc] | [next] | [standalone]


#1381735

Fromkbuild test robot <lkp@intel.com>
Date2016-04-18 16:10 +0200
Message-ID<rpi2e-6VB-35@gated-at.bofh.it>
In reply to#1381720

[Multipart message — attachments visible in raw view] — view raw

Hi Dmitry,

[auto build test WARNING on v4.6-rc4]
[also build test WARNING on next-20160418]
[cannot apply to tip/x86/core tip/x86/vdso]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Dmitry-Safonov/x86-rename-is_-ia32-x32-_task-to-in_-ia32-x32-_syscall/20160418-214656
config: x86_64-randconfig-x000-201616 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   In file included from include/asm-generic/bug.h:4:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/mm.h:8,
                    from arch/x86/entry/vdso/vma.c:7:
   arch/x86/entry/vdso/vma.c: In function 'vdso_mremap':
   arch/x86/entry/vdso/vma.c:114:37: error: 'vdso_image_32' undeclared (first use in this function)
     if (in_ia32_syscall() && image == &vdso_image_32) {
                                        ^
   include/linux/compiler.h:151:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^
>> arch/x86/entry/vdso/vma.c:114:2: note: in expansion of macro 'if'
     if (in_ia32_syscall() && image == &vdso_image_32) {
     ^
   arch/x86/entry/vdso/vma.c:114:37: note: each undeclared identifier is reported only once for each function it appears in
     if (in_ia32_syscall() && image == &vdso_image_32) {
                                        ^
   include/linux/compiler.h:151:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^
>> arch/x86/entry/vdso/vma.c:114:2: note: in expansion of macro 'if'
     if (in_ia32_syscall() && image == &vdso_image_32) {
     ^

vim +/if +114 arch/x86/entry/vdso/vma.c

     1	/*
     2	 * Copyright 2007 Andi Kleen, SUSE Labs.
     3	 * Subject to the GPL, v.2
     4	 *
     5	 * This contains most of the x86 vDSO kernel-side code.
     6	 */
   > 7	#include <linux/mm.h>
     8	#include <linux/err.h>
     9	#include <linux/sched.h>
    10	#include <linux/slab.h>
    11	#include <linux/init.h>
    12	#include <linux/random.h>
    13	#include <linux/elf.h>
    14	#include <linux/cpu.h>
    15	#include <linux/ptrace.h>
    16	#include <asm/pvclock.h>
    17	#include <asm/vgtod.h>
    18	#include <asm/proto.h>
    19	#include <asm/vdso.h>
    20	#include <asm/vvar.h>
    21	#include <asm/page.h>
    22	#include <asm/hpet.h>
    23	#include <asm/desc.h>
    24	#include <asm/cpufeature.h>
    25	
    26	#if defined(CONFIG_X86_64)
    27	unsigned int __read_mostly vdso64_enabled = 1;
    28	#endif
    29	
    30	void __init init_vdso_image(const struct vdso_image *image)
    31	{
    32		BUG_ON(image->size % PAGE_SIZE != 0);
    33	
    34		apply_alternatives((struct alt_instr *)(image->data + image->alt),
    35				   (struct alt_instr *)(image->data + image->alt +
    36							image->alt_len));
    37	}
    38	
    39	struct linux_binprm;
    40	
    41	/*
    42	 * Put the vdso above the (randomized) stack with another randomized
    43	 * offset.  This way there is no hole in the middle of address space.
    44	 * To save memory make sure it is still in the same PTE as the stack
    45	 * top.  This doesn't give that many random bits.
    46	 *
    47	 * Note that this algorithm is imperfect: the distribution of the vdso
    48	 * start address within a PMD is biased toward the end.
    49	 *
    50	 * Only used for the 64-bit and x32 vdsos.
    51	 */
    52	static unsigned long vdso_addr(unsigned long start, unsigned len)
    53	{
    54	#ifdef CONFIG_X86_32
    55		return 0;
    56	#else
    57		unsigned long addr, end;
    58		unsigned offset;
    59	
    60		/*
    61		 * Round up the start address.  It can start out unaligned as a result
    62		 * of stack start randomization.
    63		 */
    64		start = PAGE_ALIGN(start);
    65	
    66		/* Round the lowest possible end address up to a PMD boundary. */
    67		end = (start + len + PMD_SIZE - 1) & PMD_MASK;
    68		if (end >= TASK_SIZE_MAX)
    69			end = TASK_SIZE_MAX;
    70		end -= len;
    71	
    72		if (end > start) {
    73			offset = get_random_int() % (((end - start) >> PAGE_SHIFT) + 1);
    74			addr = start + (offset << PAGE_SHIFT);
    75		} else {
    76			addr = start;
    77		}
    78	
    79		/*
    80		 * Forcibly align the final address in case we have a hardware
    81		 * issue that requires alignment for performance reasons.
    82		 */
    83		addr = align_vdso_addr(addr);
    84	
    85		return addr;
    86	#endif
    87	}
    88	
    89	static int vdso_fault(const struct vm_special_mapping *sm,
    90			      struct vm_area_struct *vma, struct vm_fault *vmf)
    91	{
    92		const struct vdso_image *image = vma->vm_mm->context.vdso_image;
    93	
    94		if (!image || (vmf->pgoff << PAGE_SHIFT) >= image->size)
    95			return VM_FAULT_SIGBUS;
    96	
    97		vmf->page = virt_to_page(image->data + (vmf->pgoff << PAGE_SHIFT));
    98		get_page(vmf->page);
    99		return 0;
   100	}
   101	
   102	static int vdso_mremap(const struct vm_special_mapping *sm,
   103			      struct vm_area_struct *new_vma)
   104	{
   105		unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
   106		const struct vdso_image *image = current->mm->context.vdso_image;
   107	
   108		if (image->size != new_size)
   109			return -EINVAL;
   110	
   111		if (current->mm != new_vma->vm_mm)
   112			return -EFAULT;
   113	
 > 114		if (in_ia32_syscall() && image == &vdso_image_32) {
   115			struct pt_regs *regs = current_pt_regs();
   116			unsigned long vdso_land = image->sym_int80_landing_pad;
   117			unsigned long old_land_addr = vdso_land +

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[toc] | [prev] | [next] | [standalone]


#1381741 — [PATCHv6 2/3] x86/vdso: add mremap hook to vm_special_mapping

FromDmitry Safonov <dsafonov@virtuozzo.com>
Date2016-04-18 16:20 +0200
Subject[PATCHv6 2/3] x86/vdso: add mremap hook to vm_special_mapping
Message-ID<rpibU-6ZZ-17@gated-at.bofh.it>
In reply to#1381720
Add possibility for userspace 32-bit applications to move
vdso mapping. Previously, when userspace app called
mremap for vdso, in return path it would land on previous
address of vdso page, resulting in segmentation violation.
Now it lands fine and returns to userspace with remapped vdso.
This will also fix context.vdso pointer for 64-bit, which does not
affect the user of vdso after mremap by now, but this may change.

As suggested by Andy, return EINVAL for mremap that splits vdso image.

Renamed and moved text_mapping structure declaration inside
map_vdso, as it used only there and now it complement
vvar_mapping variable.

There is still problem for remapping vdso in glibc applications:
linker relocates addresses for syscalls on vdso page, so
you need to relink with the new addresses. Or the next syscall
through glibc may fail:
  Program received signal SIGSEGV, Segmentation fault.
  #0  0xf7fd9b80 in __kernel_vsyscall ()
  #1  0xf7ec8238 in _exit () from /usr/lib32/libc.so.6

Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
---
v6: moved vdso_image_32 check and fixup code into vdso_fix_landing function
    with ifdefs around
v5: as Andy suggested, add a check that new_vma->vm_mm and current->mm are
    the same, also check not only in_ia32_syscall() but image == &vdso_image_32
v4: drop __maybe_unused & use image from mm->context instead vdso_image_32
v3: as Andy suggested, return EINVAL in case of splitting vdso blob on mremap;
    used is_ia32_task instead of ifdefs 
v2: added __maybe_unused for pt_regs in vdso_mremap

 arch/x86/entry/vdso/vma.c | 49 ++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/mm_types.h  |  3 +++
 mm/mmap.c                 | 10 ++++++++++
 3 files changed, 57 insertions(+), 5 deletions(-)

diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 10f704584922..3385586f86a6 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -12,6 +12,7 @@
 #include <linux/random.h>
 #include <linux/elf.h>
 #include <linux/cpu.h>
+#include <linux/ptrace.h>
 #include <asm/pvclock.h>
 #include <asm/vgtod.h>
 #include <asm/proto.h>
@@ -98,10 +99,42 @@ static int vdso_fault(const struct vm_special_mapping *sm,
 	return 0;
 }
 
-static const struct vm_special_mapping text_mapping = {
-	.name = "[vdso]",
-	.fault = vdso_fault,
-};
+#if defined CONFIG_X86_32 || defined CONFIG_COMPAT
+static void vdso_fix_landing(const struct vdso_image *image,
+		struct vm_area_struct *new_vma)
+{
+	if (in_ia32_syscall() && image == &vdso_image_32) {
+		struct pt_regs *regs = current_pt_regs();
+		unsigned long vdso_land = image->sym_int80_landing_pad;
+		unsigned long old_land_addr = vdso_land +
+			(unsigned long)current->mm->context.vdso;
+
+		/* Fixing userspace landing - look at do_fast_syscall_32 */
+		if (regs->ip == old_land_addr)
+			regs->ip = new_vma->vm_start + vdso_land;
+	}
+}
+#else
+static void vdso_fix_landing(const struct vdso_image *image) {}
+#endif
+
+static int vdso_mremap(const struct vm_special_mapping *sm,
+		struct vm_area_struct *new_vma)
+{
+	unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
+	const struct vdso_image *image = current->mm->context.vdso_image;
+
+	if (image->size != new_size)
+		return -EINVAL;
+
+	if (current->mm != new_vma->vm_mm)
+		return -EFAULT;
+
+	vdso_fix_landing(image, new_vma);
+	current->mm->context.vdso = (void __user *)new_vma->vm_start;
+
+	return 0;
+}
 
 static int vvar_fault(const struct vm_special_mapping *sm,
 		      struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -162,6 +195,12 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
 	struct vm_area_struct *vma;
 	unsigned long addr, text_start;
 	int ret = 0;
+
+	static const struct vm_special_mapping vdso_mapping = {
+		.name = "[vdso]",
+		.fault = vdso_fault,
+		.mremap = vdso_mremap,
+	};
 	static const struct vm_special_mapping vvar_mapping = {
 		.name = "[vvar]",
 		.fault = vvar_fault,
@@ -195,7 +234,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
 				       image->size,
 				       VM_READ|VM_EXEC|
 				       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
-				       &text_mapping);
+				       &vdso_mapping);
 
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index c2d75b4fa86c..4d16ab9287af 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -586,6 +586,9 @@ struct vm_special_mapping {
 	int (*fault)(const struct vm_special_mapping *sm,
 		     struct vm_area_struct *vma,
 		     struct vm_fault *vmf);
+
+	int (*mremap)(const struct vm_special_mapping *sm,
+		     struct vm_area_struct *new_vma);
 };
 
 enum tlb_flush_reason {
diff --git a/mm/mmap.c b/mm/mmap.c
index bd2e1a533bc1..ba71658dd1a1 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2930,9 +2930,19 @@ static const char *special_mapping_name(struct vm_area_struct *vma)
 	return ((struct vm_special_mapping *)vma->vm_private_data)->name;
 }
 
+static int special_mapping_mremap(struct vm_area_struct *new_vma)
+{
+	struct vm_special_mapping *sm = new_vma->vm_private_data;
+
+	if (sm->mremap)
+		return sm->mremap(sm, new_vma);
+	return 0;
+}
+
 static const struct vm_operations_struct special_mapping_vmops = {
 	.close = special_mapping_close,
 	.fault = special_mapping_fault,
+	.mremap = special_mapping_mremap,
 	.name = special_mapping_name,
 };
 
-- 
2.8.0

[toc] | [prev] | [next] | [standalone]


#1381752 — [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping

FromDmitry Safonov <dsafonov@virtuozzo.com>
Date2016-04-18 16:30 +0200
Subject[PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping
Message-ID<rpilA-75e-15@gated-at.bofh.it>
In reply to#1381720
Add possibility for userspace 32-bit applications to move
vdso mapping. Previously, when userspace app called
mremap for vdso, in return path it would land on previous
address of vdso page, resulting in segmentation violation.
Now it lands fine and returns to userspace with remapped vdso.
This will also fix context.vdso pointer for 64-bit, which does not
affect the user of vdso after mremap by now, but this may change.

As suggested by Andy, return EINVAL for mremap that splits vdso image.

Renamed and moved text_mapping structure declaration inside
map_vdso, as it used only there and now it complement
vvar_mapping variable.

There is still problem for remapping vdso in glibc applications:
linker relocates addresses for syscalls on vdso page, so
you need to relink with the new addresses. Or the next syscall
through glibc may fail:
  Program received signal SIGSEGV, Segmentation fault.
  #0  0xf7fd9b80 in __kernel_vsyscall ()
  #1  0xf7ec8238 in _exit () from /usr/lib32/libc.so.6

Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
---
v7: that's just not my day: add new_vma parameter to vdso_fix_landing
    sorry for the noise
v6: moved vdso_image_32 check and fixup code into vdso_fix_landing function
    with ifdefs around
v5: as Andy suggested, add a check that new_vma->vm_mm and current->mm are
    the same, also check not only in_ia32_syscall() but image == &vdso_image_32
v4: drop __maybe_unused & use image from mm->context instead vdso_image_32
v3: as Andy suggested, return EINVAL in case of splitting vdso blob on mremap;
    used is_ia32_task instead of ifdefs 
v2: added __maybe_unused for pt_regs in vdso_mremap

 arch/x86/entry/vdso/vma.c | 50 ++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/mm_types.h  |  3 +++
 mm/mmap.c                 | 10 ++++++++++
 3 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 10f704584922..d94291a19b6e 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -12,6 +12,7 @@
 #include <linux/random.h>
 #include <linux/elf.h>
 #include <linux/cpu.h>
+#include <linux/ptrace.h>
 #include <asm/pvclock.h>
 #include <asm/vgtod.h>
 #include <asm/proto.h>
@@ -98,10 +99,43 @@ static int vdso_fault(const struct vm_special_mapping *sm,
 	return 0;
 }
 
-static const struct vm_special_mapping text_mapping = {
-	.name = "[vdso]",
-	.fault = vdso_fault,
-};
+#if defined CONFIG_X86_32 || defined CONFIG_COMPAT
+static void vdso_fix_landing(const struct vdso_image *image,
+		struct vm_area_struct *new_vma)
+{
+	if (in_ia32_syscall() && image == &vdso_image_32) {
+		struct pt_regs *regs = current_pt_regs();
+		unsigned long vdso_land = image->sym_int80_landing_pad;
+		unsigned long old_land_addr = vdso_land +
+			(unsigned long)current->mm->context.vdso;
+
+		/* Fixing userspace landing - look at do_fast_syscall_32 */
+		if (regs->ip == old_land_addr)
+			regs->ip = new_vma->vm_start + vdso_land;
+	}
+}
+#else
+static void vdso_fix_landing(const struct vdso_image *image,
+		struct vm_area_struct *new_vma) {}
+#endif
+
+static int vdso_mremap(const struct vm_special_mapping *sm,
+		struct vm_area_struct *new_vma)
+{
+	unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
+	const struct vdso_image *image = current->mm->context.vdso_image;
+
+	if (image->size != new_size)
+		return -EINVAL;
+
+	if (current->mm != new_vma->vm_mm)
+		return -EFAULT;
+
+	vdso_fix_landing(image, new_vma);
+	current->mm->context.vdso = (void __user *)new_vma->vm_start;
+
+	return 0;
+}
 
 static int vvar_fault(const struct vm_special_mapping *sm,
 		      struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -162,6 +196,12 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
 	struct vm_area_struct *vma;
 	unsigned long addr, text_start;
 	int ret = 0;
+
+	static const struct vm_special_mapping vdso_mapping = {
+		.name = "[vdso]",
+		.fault = vdso_fault,
+		.mremap = vdso_mremap,
+	};
 	static const struct vm_special_mapping vvar_mapping = {
 		.name = "[vvar]",
 		.fault = vvar_fault,
@@ -195,7 +235,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
 				       image->size,
 				       VM_READ|VM_EXEC|
 				       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
-				       &text_mapping);
+				       &vdso_mapping);
 
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index c2d75b4fa86c..4d16ab9287af 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -586,6 +586,9 @@ struct vm_special_mapping {
 	int (*fault)(const struct vm_special_mapping *sm,
 		     struct vm_area_struct *vma,
 		     struct vm_fault *vmf);
+
+	int (*mremap)(const struct vm_special_mapping *sm,
+		     struct vm_area_struct *new_vma);
 };
 
 enum tlb_flush_reason {
diff --git a/mm/mmap.c b/mm/mmap.c
index bd2e1a533bc1..ba71658dd1a1 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2930,9 +2930,19 @@ static const char *special_mapping_name(struct vm_area_struct *vma)
 	return ((struct vm_special_mapping *)vma->vm_private_data)->name;
 }
 
+static int special_mapping_mremap(struct vm_area_struct *new_vma)
+{
+	struct vm_special_mapping *sm = new_vma->vm_private_data;
+
+	if (sm->mremap)
+		return sm->mremap(sm, new_vma);
+	return 0;
+}
+
 static const struct vm_operations_struct special_mapping_vmops = {
 	.close = special_mapping_close,
 	.fault = special_mapping_fault,
+	.mremap = special_mapping_mremap,
 	.name = special_mapping_name,
 };
 
-- 
2.8.0

[toc] | [prev] | [next] | [standalone]


#1383555 — Re: [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping

FromDmitry Safonov <dsafonov@virtuozzo.com>
Date2016-04-20 18:40 +0200
SubjectRe: [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping
Message-ID<rq3kt-2o0-1@gated-at.bofh.it>
In reply to#1381752
On 04/18/2016 05:23 PM, Dmitry Safonov wrote:
> Add possibility for userspace 32-bit applications to move
> vdso mapping. Previously, when userspace app called
> mremap for vdso, in return path it would land on previous
> address of vdso page, resulting in segmentation violation.
> Now it lands fine and returns to userspace with remapped vdso.
> This will also fix context.vdso pointer for 64-bit, which does not
> affect the user of vdso after mremap by now, but this may change.
>
> As suggested by Andy, return EINVAL for mremap that splits vdso image.
>
> Renamed and moved text_mapping structure declaration inside
> map_vdso, as it used only there and now it complement
> vvar_mapping variable.
>
> There is still problem for remapping vdso in glibc applications:
> linker relocates addresses for syscalls on vdso page, so
> you need to relink with the new addresses. Or the next syscall
> through glibc may fail:
>    Program received signal SIGSEGV, Segmentation fault.
>    #0  0xf7fd9b80 in __kernel_vsyscall ()
>    #1  0xf7ec8238 in _exit () from /usr/lib32/libc.so.6
>
> Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>

Andy, can I have an ack from you for this version?
If it looks fine for you, of course.
And for v5 test (3/3 patch)? (I did not resed it, as it hasn't changed)

> ---
> v7: that's just not my day: add new_vma parameter to vdso_fix_landing
>      sorry for the noise
> v6: moved vdso_image_32 check and fixup code into vdso_fix_landing function
>      with ifdefs around
> v5: as Andy suggested, add a check that new_vma->vm_mm and current->mm are
>      the same, also check not only in_ia32_syscall() but image == &vdso_image_32
> v4: drop __maybe_unused & use image from mm->context instead vdso_image_32
> v3: as Andy suggested, return EINVAL in case of splitting vdso blob on mremap;
>      used is_ia32_task instead of ifdefs
> v2: added __maybe_unused for pt_regs in vdso_mremap
>
>   arch/x86/entry/vdso/vma.c | 50 ++++++++++++++++++++++++++++++++++++++++++-----
>   include/linux/mm_types.h  |  3 +++
>   mm/mmap.c                 | 10 ++++++++++
>   3 files changed, 58 insertions(+), 5 deletions(-)
>
> diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
> index 10f704584922..d94291a19b6e 100644
> --- a/arch/x86/entry/vdso/vma.c
> +++ b/arch/x86/entry/vdso/vma.c
> @@ -12,6 +12,7 @@
>   #include <linux/random.h>
>   #include <linux/elf.h>
>   #include <linux/cpu.h>
> +#include <linux/ptrace.h>
>   #include <asm/pvclock.h>
>   #include <asm/vgtod.h>
>   #include <asm/proto.h>
> @@ -98,10 +99,43 @@ static int vdso_fault(const struct vm_special_mapping *sm,
>   	return 0;
>   }
>   
> -static const struct vm_special_mapping text_mapping = {
> -	.name = "[vdso]",
> -	.fault = vdso_fault,
> -};
> +#if defined CONFIG_X86_32 || defined CONFIG_COMPAT
> +static void vdso_fix_landing(const struct vdso_image *image,
> +		struct vm_area_struct *new_vma)
> +{
> +	if (in_ia32_syscall() && image == &vdso_image_32) {
> +		struct pt_regs *regs = current_pt_regs();
> +		unsigned long vdso_land = image->sym_int80_landing_pad;
> +		unsigned long old_land_addr = vdso_land +
> +			(unsigned long)current->mm->context.vdso;
> +
> +		/* Fixing userspace landing - look at do_fast_syscall_32 */
> +		if (regs->ip == old_land_addr)
> +			regs->ip = new_vma->vm_start + vdso_land;
> +	}
> +}
> +#else
> +static void vdso_fix_landing(const struct vdso_image *image,
> +		struct vm_area_struct *new_vma) {}
> +#endif
> +
> +static int vdso_mremap(const struct vm_special_mapping *sm,
> +		struct vm_area_struct *new_vma)
> +{
> +	unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
> +	const struct vdso_image *image = current->mm->context.vdso_image;
> +
> +	if (image->size != new_size)
> +		return -EINVAL;
> +
> +	if (current->mm != new_vma->vm_mm)
> +		return -EFAULT;
> +
> +	vdso_fix_landing(image, new_vma);
> +	current->mm->context.vdso = (void __user *)new_vma->vm_start;
> +
> +	return 0;
> +}
>   
>   static int vvar_fault(const struct vm_special_mapping *sm,
>   		      struct vm_area_struct *vma, struct vm_fault *vmf)
> @@ -162,6 +196,12 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
>   	struct vm_area_struct *vma;
>   	unsigned long addr, text_start;
>   	int ret = 0;
> +
> +	static const struct vm_special_mapping vdso_mapping = {
> +		.name = "[vdso]",
> +		.fault = vdso_fault,
> +		.mremap = vdso_mremap,
> +	};
>   	static const struct vm_special_mapping vvar_mapping = {
>   		.name = "[vvar]",
>   		.fault = vvar_fault,
> @@ -195,7 +235,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
>   				       image->size,
>   				       VM_READ|VM_EXEC|
>   				       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
> -				       &text_mapping);
> +				       &vdso_mapping);
>   
>   	if (IS_ERR(vma)) {
>   		ret = PTR_ERR(vma);
> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
> index c2d75b4fa86c..4d16ab9287af 100644
> --- a/include/linux/mm_types.h
> +++ b/include/linux/mm_types.h
> @@ -586,6 +586,9 @@ struct vm_special_mapping {
>   	int (*fault)(const struct vm_special_mapping *sm,
>   		     struct vm_area_struct *vma,
>   		     struct vm_fault *vmf);
> +
> +	int (*mremap)(const struct vm_special_mapping *sm,
> +		     struct vm_area_struct *new_vma);
>   };
>   
>   enum tlb_flush_reason {
> diff --git a/mm/mmap.c b/mm/mmap.c
> index bd2e1a533bc1..ba71658dd1a1 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -2930,9 +2930,19 @@ static const char *special_mapping_name(struct vm_area_struct *vma)
>   	return ((struct vm_special_mapping *)vma->vm_private_data)->name;
>   }
>   
> +static int special_mapping_mremap(struct vm_area_struct *new_vma)
> +{
> +	struct vm_special_mapping *sm = new_vma->vm_private_data;
> +
> +	if (sm->mremap)
> +		return sm->mremap(sm, new_vma);
> +	return 0;
> +}
> +
>   static const struct vm_operations_struct special_mapping_vmops = {
>   	.close = special_mapping_close,
>   	.fault = special_mapping_fault,
> +	.mremap = special_mapping_mremap,
>   	.name = special_mapping_name,
>   };
>   


-- 
Regards,
Dmitry Safonov

[toc] | [prev] | [next] | [standalone]


#1384541 — Re: [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping

FromAndy Lutomirski <luto@amacapital.net>
Date2016-04-21 22:00 +0200
SubjectRe: [PATCHv7 2/3] x86/vdso: add mremap hook to vm_special_mapping
Message-ID<rqsVA-6gR-21@gated-at.bofh.it>
In reply to#1381752
On Mon, Apr 18, 2016 at 7:23 AM, Dmitry Safonov <dsafonov@virtuozzo.com> wrote:
> Add possibility for userspace 32-bit applications to move
> vdso mapping. Previously, when userspace app called
> mremap for vdso, in return path it would land on previous
> address of vdso page, resulting in segmentation violation.
> Now it lands fine and returns to userspace with remapped vdso.
> This will also fix context.vdso pointer for 64-bit, which does not
> affect the user of vdso after mremap by now, but this may change.
>
> As suggested by Andy, return EINVAL for mremap that splits vdso image.
>
> Renamed and moved text_mapping structure declaration inside
> map_vdso, as it used only there and now it complement
> vvar_mapping variable.
>
> There is still problem for remapping vdso in glibc applications:
> linker relocates addresses for syscalls on vdso page, so
> you need to relink with the new addresses. Or the next syscall
> through glibc may fail:
>   Program received signal SIGSEGV, Segmentation fault.
>   #0  0xf7fd9b80 in __kernel_vsyscall ()
>   #1  0xf7ec8238 in _exit () from /usr/lib32/libc.so.6
>
> Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
> ---
> v7: that's just not my day: add new_vma parameter to vdso_fix_landing
>     sorry for the noise
> v6: moved vdso_image_32 check and fixup code into vdso_fix_landing function
>     with ifdefs around
> v5: as Andy suggested, add a check that new_vma->vm_mm and current->mm are
>     the same, also check not only in_ia32_syscall() but image == &vdso_image_32
> v4: drop __maybe_unused & use image from mm->context instead vdso_image_32
> v3: as Andy suggested, return EINVAL in case of splitting vdso blob on mremap;
>     used is_ia32_task instead of ifdefs
> v2: added __maybe_unused for pt_regs in vdso_mremap
>
>  arch/x86/entry/vdso/vma.c | 50 ++++++++++++++++++++++++++++++++++++++++++-----
>  include/linux/mm_types.h  |  3 +++
>  mm/mmap.c                 | 10 ++++++++++
>  3 files changed, 58 insertions(+), 5 deletions(-)
>
> diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
> index 10f704584922..d94291a19b6e 100644
> --- a/arch/x86/entry/vdso/vma.c
> +++ b/arch/x86/entry/vdso/vma.c
> @@ -12,6 +12,7 @@
>  #include <linux/random.h>
>  #include <linux/elf.h>
>  #include <linux/cpu.h>
> +#include <linux/ptrace.h>
>  #include <asm/pvclock.h>
>  #include <asm/vgtod.h>
>  #include <asm/proto.h>
> @@ -98,10 +99,43 @@ static int vdso_fault(const struct vm_special_mapping *sm,
>         return 0;
>  }
>
> -static const struct vm_special_mapping text_mapping = {
> -       .name = "[vdso]",
> -       .fault = vdso_fault,
> -};
> +#if defined CONFIG_X86_32 || defined CONFIG_COMPAT

I think you mean defined(CONFIG_IA32_EMULATION).  IIRC CONFIG_COMPAT
is set for X32, !IA32 kernels, too.

> +static void vdso_fix_landing(const struct vdso_image *image,
> +               struct vm_area_struct *new_vma)
> +{
> +       if (in_ia32_syscall() && image == &vdso_image_32) {
> +               struct pt_regs *regs = current_pt_regs();
> +               unsigned long vdso_land = image->sym_int80_landing_pad;
> +               unsigned long old_land_addr = vdso_land +
> +                       (unsigned long)current->mm->context.vdso;
> +
> +               /* Fixing userspace landing - look at do_fast_syscall_32 */
> +               if (regs->ip == old_land_addr)
> +                       regs->ip = new_vma->vm_start + vdso_land;
> +       }
> +}
> +#else
> +static void vdso_fix_landing(const struct vdso_image *image,
> +               struct vm_area_struct *new_vma) {}
> +#endif

You could also define the function regardless and simply ifdef out the
body, thus saving a few lines of code.

> +
> +static int vdso_mremap(const struct vm_special_mapping *sm,
> +               struct vm_area_struct *new_vma)
> +{
> +       unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
> +       const struct vdso_image *image = current->mm->context.vdso_image;
> +
> +       if (image->size != new_size)
> +               return -EINVAL;
> +
> +       if (current->mm != new_vma->vm_mm)
> +               return -EFAULT;

Can you make that if (WARN_ON_ONCE(current->mm != new_vma->vm_mm))?
It should be impossible.

> +
> +       vdso_fix_landing(image, new_vma);
> +       current->mm->context.vdso = (void __user *)new_vma->vm_start;
> +
> +       return 0;
> +}
>
>  static int vvar_fault(const struct vm_special_mapping *sm,
>                       struct vm_area_struct *vma, struct vm_fault *vmf)
> @@ -162,6 +196,12 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
>         struct vm_area_struct *vma;
>         unsigned long addr, text_start;
>         int ret = 0;
> +
> +       static const struct vm_special_mapping vdso_mapping = {
> +               .name = "[vdso]",
> +               .fault = vdso_fault,
> +               .mremap = vdso_mremap,
> +       };
>         static const struct vm_special_mapping vvar_mapping = {
>                 .name = "[vvar]",
>                 .fault = vvar_fault,
> @@ -195,7 +235,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
>                                        image->size,
>                                        VM_READ|VM_EXEC|
>                                        VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
> -                                      &text_mapping);
> +                                      &vdso_mapping);
>
>         if (IS_ERR(vma)) {
>                 ret = PTR_ERR(vma);
> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
> index c2d75b4fa86c..4d16ab9287af 100644
> --- a/include/linux/mm_types.h
> +++ b/include/linux/mm_types.h
> @@ -586,6 +586,9 @@ struct vm_special_mapping {
>         int (*fault)(const struct vm_special_mapping *sm,
>                      struct vm_area_struct *vma,
>                      struct vm_fault *vmf);
> +
> +       int (*mremap)(const struct vm_special_mapping *sm,
> +                    struct vm_area_struct *new_vma);
>  };
>
>  enum tlb_flush_reason {
> diff --git a/mm/mmap.c b/mm/mmap.c
> index bd2e1a533bc1..ba71658dd1a1 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -2930,9 +2930,19 @@ static const char *special_mapping_name(struct vm_area_struct *vma)
>         return ((struct vm_special_mapping *)vma->vm_private_data)->name;
>  }
>
> +static int special_mapping_mremap(struct vm_area_struct *new_vma)
> +{
> +       struct vm_special_mapping *sm = new_vma->vm_private_data;
> +
> +       if (sm->mremap)
> +               return sm->mremap(sm, new_vma);
> +       return 0;
> +}
> +
>  static const struct vm_operations_struct special_mapping_vmops = {
>         .close = special_mapping_close,
>         .fault = special_mapping_fault,
> +       .mremap = special_mapping_mremap,
>         .name = special_mapping_name,
>  };
>
> --
> 2.8.0
>



-- 
Andy Lutomirski
AMA Capital Management, LLC

[toc] | [prev] | [next] | [standalone]


#1385741

Fromkbuild test robot <lkp@intel.com>
Date2016-04-24 01:20 +0200
Message-ID<rrf0d-2Qv-5@gated-at.bofh.it>
In reply to#1381720

[Multipart message — attachments visible in raw view] — view raw

Hi,

[auto build test ERROR on v4.6-rc4]
[cannot apply to tip/x86/core tip/x86/vdso next-20160422]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Dmitry-Safonov/x86-rename-is_-ia32-x32-_task-to-in_-ia32-x32-_syscall/20160418-214656
config: x86_64-randconfig-s0-04240623 (attached as .config)
compiler: gcc-5 (Debian 5.3.1-14) 5.3.1 20160409
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

Note: the linux-review/Dmitry-Safonov/x86-rename-is_-ia32-x32-_task-to-in_-ia32-x32-_syscall/20160418-214656 HEAD aed83f1dd951908724a5ba564e2b03d68a9fc7b8 builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   arch/x86/entry/vdso/vma.c: In function 'vdso_mremap':
>> arch/x86/entry/vdso/vma.c:114:37: error: 'vdso_image_32' undeclared (first use in this function)
     if (in_ia32_syscall() && image == &vdso_image_32) {
                                        ^
   arch/x86/entry/vdso/vma.c:114:37: note: each undeclared identifier is reported only once for each function it appears in

vim +/vdso_image_32 +114 arch/x86/entry/vdso/vma.c

   108		if (image->size != new_size)
   109			return -EINVAL;
   110	
   111		if (current->mm != new_vma->vm_mm)
   112			return -EFAULT;
   113	
 > 114		if (in_ia32_syscall() && image == &vdso_image_32) {
   115			struct pt_regs *regs = current_pt_regs();
   116			unsigned long vdso_land = image->sym_int80_landing_pad;
   117			unsigned long old_land_addr = vdso_land +

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web