diff mbox

[RFC] uprobes: copy to user-space xol page with proper cache flushing

Message ID CAA3XUr0-KqtBw0WCFvws8g5ChK49d+X+7Un9ngq1UZg-oROx+A@mail.gmail.com
State New
Headers show

Commit Message

vkamensky April 11, 2014, 2:26 p.m. UTC
On 10 April 2014 21:36, David Miller <davem@davemloft.net> wrote:
> From: David Long <dave.long@linaro.org>
> Date: Thu, 10 Apr 2014 23:45:31 -0400
>
>> Replace memcpy and dcache flush in generic uprobes with a call to
>> copy_to_user_page(), which will do a proper flushing of kernel and
>> user cache.  Also modify the inmplementation of copy_to_user_page
>> to assume a NULL vma pointer means the user icache corresponding
>> to this right is stale and needs to be flushed.  Note that this patch
>> does not fix copy_to_user page for the sh, alpha, sparc, or mips
>> architectures (which do not currently support uprobes).
>>
>> Signed-off-by: David A. Long <dave.long@linaro.org>
>
> You really need to pass the proper VMA down to the call site
> rather than pass NULL, that's extremely ugly and totally
> unnecesary.

Agreed that VMA is really needed.

Here is variant that I tried while waiting for Oleg's response:

From 4a6a9043e0910041dd8842835a528cbdc39fad34 Mon Sep 17 00:00:00 2001
From: Victor Kamensky <victor.kamensky@linaro.org>
Date: Thu, 10 Apr 2014 17:06:39 -0700
Subject: [PATCH] uprobes: use copy_to_user_page function to copy instr to xol
 area

Use copy_to_user_page function to copy instruction into xol area.
copy_to_user_page function guarantee that all caches are correctly
flushed during such write (including icache as well if needed).

Because copy_to_user_page needs vm_area_struct, vma
field was added into struct xol_area. It holds cached vma value
for xol_area.

Also using copy_to_user_page we make sure that we use the same
code that ptrace write code uses.

Signed-off-by: Victor Kamensky <victor.kamensky@linaro.org>
---
 kernel/events/uprobes.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)


@@ -1287,6 +1289,7 @@ static unsigned long xol_get_insn_slot(struct
uprobe *uprobe)
 {
     struct xol_area *area;
     unsigned long xol_vaddr;
+    void *xol_page_kaddr;

     area = get_xol_area();
     if (!area)
@@ -1297,8 +1300,11 @@ static unsigned long xol_get_insn_slot(struct
uprobe *uprobe)
         return 0;

     /* Initialize the slot */
-    copy_to_page(area->page, xol_vaddr,
-            &uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
+    xol_page_kaddr = kmap_atomic(area->page);
+    copy_to_user_page(area->vma, area->page, xol_vaddr,
+              xol_page_kaddr + (xol_vaddr & ~PAGE_MASK),
+              &uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
+    kunmap_atomic(xol_page_kaddr);
     /*
      * We probably need flush_icache_user_range() but it needs vma.
      * This should work on supported architectures too.

Comments

Oleg Nesterov April 11, 2014, 2:35 p.m. UTC | #1
On 04/11, Victor Kamensky wrote:
>
> On 10 April 2014 21:36, David Miller <davem@davemloft.net> wrote:
> > You really need to pass the proper VMA down to the call site
> > rather than pass NULL, that's extremely ugly and totally
> > unnecesary.
>
> Agreed that VMA is really needed.

I do not ;) but I am still trying to finish my email...

> index 04709b6..1ae4563 100644
> --- a/kernel/events/uprobes.c
> +++ b/kernel/events/uprobes.c
> @@ -117,6 +117,7 @@ struct xol_area {
>       * the vma go away, and we must handle that reasonably gracefully.
>       */
>      unsigned long         vaddr;        /* Page(s) of instruction slots */
> +    struct vm_area_struct    *vma;        /* VMA that holds above address */
>  };
> 
>  /*
> @@ -1150,6 +1151,7 @@ static int xol_add_vma(struct mm_struct *mm,
> struct xol_area *area)
> 
>      ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE,
>                  VM_EXEC|VM_MAYEXEC|VM_DONTCOPY|VM_IO, &area->page);
> +    area->vma = find_vma(mm, area->vaddr);

No, this can't work. This vma can be unmapped/freed/etc.

Oleg.
vkamensky April 11, 2014, 2:55 p.m. UTC | #2
On 11 April 2014 07:26, Victor Kamensky <victor.kamensky@linaro.org> wrote:
> On 10 April 2014 21:36, David Miller <davem@davemloft.net> wrote:
>> From: David Long <dave.long@linaro.org>
>> Date: Thu, 10 Apr 2014 23:45:31 -0400
>>
>>> Replace memcpy and dcache flush in generic uprobes with a call to
>>> copy_to_user_page(), which will do a proper flushing of kernel and
>>> user cache.  Also modify the inmplementation of copy_to_user_page
>>> to assume a NULL vma pointer means the user icache corresponding
>>> to this right is stale and needs to be flushed.  Note that this patch
>>> does not fix copy_to_user page for the sh, alpha, sparc, or mips
>>> architectures (which do not currently support uprobes).
>>>
>>> Signed-off-by: David A. Long <dave.long@linaro.org>
>>
>> You really need to pass the proper VMA down to the call site
>> rather than pass NULL, that's extremely ugly and totally
>> unnecesary.
>
> Agreed that VMA is really needed.
>
> Here is variant that I tried while waiting for Oleg's response:
>
> From 4a6a9043e0910041dd8842835a528cbdc39fad34 Mon Sep 17 00:00:00 2001
> From: Victor Kamensky <victor.kamensky@linaro.org>
> Date: Thu, 10 Apr 2014 17:06:39 -0700
> Subject: [PATCH] uprobes: use copy_to_user_page function to copy instr to xol
>  area
>
> Use copy_to_user_page function to copy instruction into xol area.
> copy_to_user_page function guarantee that all caches are correctly
> flushed during such write (including icache as well if needed).
>
> Because copy_to_user_page needs vm_area_struct, vma
> field was added into struct xol_area. It holds cached vma value
> for xol_area.
>
> Also using copy_to_user_page we make sure that we use the same
> code that ptrace write code uses.
>
> Signed-off-by: Victor Kamensky <victor.kamensky@linaro.org>
> ---
>  kernel/events/uprobes.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
> index 04709b6..1ae4563 100644
> --- a/kernel/events/uprobes.c
> +++ b/kernel/events/uprobes.c
> @@ -117,6 +117,7 @@ struct xol_area {
>       * the vma go away, and we must handle that reasonably gracefully.
>       */
>      unsigned long         vaddr;        /* Page(s) of instruction slots */
> +    struct vm_area_struct    *vma;        /* VMA that holds above address */
>  };
>
>  /*
> @@ -1150,6 +1151,7 @@ static int xol_add_vma(struct mm_struct *mm,
> struct xol_area *area)
>
>      ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE,
>                  VM_EXEC|VM_MAYEXEC|VM_DONTCOPY|VM_IO, &area->page);
> +    area->vma = find_vma(mm, area->vaddr);
>      if (ret)
>          goto fail;
>
> @@ -1287,6 +1289,7 @@ static unsigned long xol_get_insn_slot(struct
> uprobe *uprobe)
>  {
>      struct xol_area *area;
>      unsigned long xol_vaddr;
> +    void *xol_page_kaddr;
>
>      area = get_xol_area();
>      if (!area)
> @@ -1297,8 +1300,11 @@ static unsigned long xol_get_insn_slot(struct
> uprobe *uprobe)
>          return 0;
>
>      /* Initialize the slot */
> -    copy_to_page(area->page, xol_vaddr,
> -            &uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
> +    xol_page_kaddr = kmap_atomic(area->page);
> +    copy_to_user_page(area->vma, area->page, xol_vaddr,
> +              xol_page_kaddr + (xol_vaddr & ~PAGE_MASK),
> +              &uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
> +    kunmap_atomic(xol_page_kaddr);
>      /*
>       * We probably need flush_icache_user_range() but it needs vma.
>       * This should work on supported architectures too.

Above comment and following call to 'flush_dcache_page(area->page);'
should be removed of course. Just noticed that ...

Thanks,
Victor

> --
> 1.8.1.4
>
> Now while waiting for Oleg's response I am trying to run some performance
> tests between different solution to understand situation better.
>
> One of my concerns was ARM smp function call that
> could be reached from copy_to_user_page. But I have hard time
> finding CPU that really doing it ... It does not look such call
> will happen on relatively modern ARM CPUs (tried Arndale,
> and Pandaboard ES). So my issues with use
> of copy_to_user_page are quite elevated.
>
> Thanks,
> Victor
diff mbox

Patch

diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 04709b6..1ae4563 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -117,6 +117,7 @@  struct xol_area {
      * the vma go away, and we must handle that reasonably gracefully.
      */
     unsigned long         vaddr;        /* Page(s) of instruction slots */
+    struct vm_area_struct    *vma;        /* VMA that holds above address */
 };

 /*
@@ -1150,6 +1151,7 @@  static int xol_add_vma(struct mm_struct *mm,
struct xol_area *area)

     ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE,
                 VM_EXEC|VM_MAYEXEC|VM_DONTCOPY|VM_IO, &area->page);
+    area->vma = find_vma(mm, area->vaddr);
     if (ret)
         goto fail;