diff mbox series

[2/3] arm64: use PAGE_KERNEL_ROX directly in alloc_insn_page

Message ID 20200618064307.32739-3-hch@lst.de (mailing list archive)
State New, archived
Headers show
Series [1/3] x86/hyperv: allocate the hypercall page with only read and execute bits | expand

Commit Message

Christoph Hellwig June 18, 2020, 6:43 a.m. UTC
Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
page read-only just after the allocation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 arch/arm64/kernel/probes/kprobes.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

Comments

David Hildenbrand June 18, 2020, 8:55 a.m. UTC | #1
On 18.06.20 08:43, Christoph Hellwig wrote:
> Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> page read-only just after the allocation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  arch/arm64/kernel/probes/kprobes.c | 12 +++---------
>  1 file changed, 3 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> index d1c95dcf1d7833..cbe49cd117cfec 100644
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>  
>  void *alloc_insn_page(void)
>  {
> -	void *page;
> -
> -	page = vmalloc_exec(PAGE_SIZE);
> -	if (page) {
> -		set_memory_ro((unsigned long)page, 1);
> -		set_vm_flush_reset_perms(page);
> -	}
> -
> -	return page;
> +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> +			NUMA_NO_NODE, __func__);

I do wonder if something like vmalloc_prot(size, prot) would make this
(and the other two users) easier to read.

So instead of ripping out vmalloc_exec(), converting it into
vmalloc_prot() instead.

Did you consider that?

Apart from that LGTM

>  }
>  
>  /* arm kprobe: install breakpoint in text */
>
Peter Zijlstra June 18, 2020, 9:27 a.m. UTC | #2
On Thu, Jun 18, 2020 at 08:43:06AM +0200, Christoph Hellwig wrote:
> Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> page read-only just after the allocation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  arch/arm64/kernel/probes/kprobes.c | 12 +++---------
>  1 file changed, 3 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> index d1c95dcf1d7833..cbe49cd117cfec 100644
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>  
>  void *alloc_insn_page(void)
>  {
> -	void *page;
> -
> -	page = vmalloc_exec(PAGE_SIZE);
> -	if (page) {
> -		set_memory_ro((unsigned long)page, 1);
> -		set_vm_flush_reset_perms(page);
> -	}
> -
> -	return page;
> +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> +			NUMA_NO_NODE, __func__);
>  }

I think this has the exact same range issue as the x86 user. But it
might be less fatal if their PLT magic can cover the full range.
Peter Zijlstra June 18, 2020, 10:35 a.m. UTC | #3
On Thu, Jun 18, 2020 at 10:55:58AM +0200, David Hildenbrand wrote:
> On 18.06.20 08:43, Christoph Hellwig wrote:
> > Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> > page read-only just after the allocation.
> > 
> > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > ---
> >  arch/arm64/kernel/probes/kprobes.c | 12 +++---------
> >  1 file changed, 3 insertions(+), 9 deletions(-)
> > 
> > diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> > index d1c95dcf1d7833..cbe49cd117cfec 100644
> > --- a/arch/arm64/kernel/probes/kprobes.c
> > +++ b/arch/arm64/kernel/probes/kprobes.c
> > @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
> >  
> >  void *alloc_insn_page(void)
> >  {
> > -	void *page;
> > -
> > -	page = vmalloc_exec(PAGE_SIZE);
> > -	if (page) {
> > -		set_memory_ro((unsigned long)page, 1);
> > -		set_vm_flush_reset_perms(page);
> > -	}
> > -
> > -	return page;
> > +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > +			NUMA_NO_NODE, __func__);
> 
> I do wonder if something like vmalloc_prot(size, prot) would make this
> (and the other two users) easier to read.
> 
> So instead of ripping out vmalloc_exec(), converting it into
> vmalloc_prot() instead.
> 
> Did you consider that?

For x86 Christoph did module_alloc_prot(), which is in his more
extensive set of patches addressing this. I suspect that would be the
right thing for ARM64 as well.
Christoph Hellwig June 18, 2020, 1:50 p.m. UTC | #4
On Thu, Jun 18, 2020 at 12:35:06PM +0200, Peter Zijlstra wrote:
> On Thu, Jun 18, 2020 at 10:55:58AM +0200, David Hildenbrand wrote:
> > On 18.06.20 08:43, Christoph Hellwig wrote:
> > > Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> > > page read-only just after the allocation.
> > > 
> > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > ---
> > >  arch/arm64/kernel/probes/kprobes.c | 12 +++---------
> > >  1 file changed, 3 insertions(+), 9 deletions(-)
> > > 
> > > diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> > > index d1c95dcf1d7833..cbe49cd117cfec 100644
> > > --- a/arch/arm64/kernel/probes/kprobes.c
> > > +++ b/arch/arm64/kernel/probes/kprobes.c
> > > @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
> > >  
> > >  void *alloc_insn_page(void)
> > >  {
> > > -	void *page;
> > > -
> > > -	page = vmalloc_exec(PAGE_SIZE);
> > > -	if (page) {
> > > -		set_memory_ro((unsigned long)page, 1);
> > > -		set_vm_flush_reset_perms(page);
> > > -	}
> > > -
> > > -	return page;
> > > +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > > +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > > +			NUMA_NO_NODE, __func__);
> > 
> > I do wonder if something like vmalloc_prot(size, prot) would make this
> > (and the other two users) easier to read.
> > 
> > So instead of ripping out vmalloc_exec(), converting it into
> > vmalloc_prot() instead.
> > 
> > Did you consider that?
> 
> For x86 Christoph did module_alloc_prot(), which is in his more
> extensive set of patches addressing this. I suspect that would be the
> right thing for ARM64 as well.

Yes.  The somewhat hacky way I added it cause problems for UML, so I
instead plan to do a series converting all architectures over to
module_alloc_prot, plus lots of other cleanups in the area that I
noticed.

I don't think vmalloc_prot is a good idea per se, as there only few
potential users, and I don't want too many vmalloc APIs.
Andrew Morton June 21, 2020, 2:16 a.m. UTC | #5
On Thu, 18 Jun 2020 08:43:06 +0200 Christoph Hellwig <hch@lst.de> wrote:

> Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> page read-only just after the allocation.
> 
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>  
>  void *alloc_insn_page(void)
>  {
> -	void *page;
> -
> -	page = vmalloc_exec(PAGE_SIZE);
> -	if (page) {
> -		set_memory_ro((unsigned long)page, 1);
> -		set_vm_flush_reset_perms(page);
> -	}
> -
> -	return page;
> +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> +			NUMA_NO_NODE, __func__);
>  }
>  
>  /* arm kprobe: install breakpoint in text */

But why.  I think this is just a cleanup, doesn't address any runtime issue?
Christoph Hellwig June 23, 2020, 9:05 a.m. UTC | #6
On Sat, Jun 20, 2020 at 07:16:16PM -0700, Andrew Morton wrote:
> On Thu, 18 Jun 2020 08:43:06 +0200 Christoph Hellwig <hch@lst.de> wrote:
> 
> > Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> > page read-only just after the allocation.
> > 
> > --- a/arch/arm64/kernel/probes/kprobes.c
> > +++ b/arch/arm64/kernel/probes/kprobes.c
> > @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
> >  
> >  void *alloc_insn_page(void)
> >  {
> > -	void *page;
> > -
> > -	page = vmalloc_exec(PAGE_SIZE);
> > -	if (page) {
> > -		set_memory_ro((unsigned long)page, 1);
> > -		set_vm_flush_reset_perms(page);
> > -	}
> > -
> > -	return page;
> > +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > +			NUMA_NO_NODE, __func__);
> >  }
> >  
> >  /* arm kprobe: install breakpoint in text */
> 
> But why.  I think this is just a cleanup, doesn't address any runtime issue?

It doesn't "fix" an issue - it just simplifies and speeds up the code.
Will Deacon June 23, 2020, 9:07 a.m. UTC | #7
On Tue, Jun 23, 2020 at 11:05:05AM +0200, Christoph Hellwig wrote:
> On Sat, Jun 20, 2020 at 07:16:16PM -0700, Andrew Morton wrote:
> > On Thu, 18 Jun 2020 08:43:06 +0200 Christoph Hellwig <hch@lst.de> wrote:
> > 
> > > Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> > > page read-only just after the allocation.
> > > 
> > > --- a/arch/arm64/kernel/probes/kprobes.c
> > > +++ b/arch/arm64/kernel/probes/kprobes.c
> > > @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
> > >  
> > >  void *alloc_insn_page(void)
> > >  {
> > > -	void *page;
> > > -
> > > -	page = vmalloc_exec(PAGE_SIZE);
> > > -	if (page) {
> > > -		set_memory_ro((unsigned long)page, 1);
> > > -		set_vm_flush_reset_perms(page);
> > > -	}
> > > -
> > > -	return page;
> > > +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > > +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > > +			NUMA_NO_NODE, __func__);
> > >  }
> > >  
> > >  /* arm kprobe: install breakpoint in text */
> > 
> > But why.  I think this is just a cleanup, doesn't address any runtime issue?
> 
> It doesn't "fix" an issue - it just simplifies and speeds up the code.

Ok, but I don't understand the PLT comment from Peter in
20200618092754.GF576905@hirez.programming.kicks-ass.net:

  | I think this has the exact same range issue as the x86 user. But it
  | might be less fatal if their PLT magic can cover the full range.

Peter, please could you elaborate on your concern? I feel like I'm missing
some context.

Cheers,

Will
Peter Zijlstra June 23, 2020, 9:37 a.m. UTC | #8
On Tue, Jun 23, 2020 at 10:07:58AM +0100, Will Deacon wrote:
> On Tue, Jun 23, 2020 at 11:05:05AM +0200, Christoph Hellwig wrote:
> > On Sat, Jun 20, 2020 at 07:16:16PM -0700, Andrew Morton wrote:
> > > On Thu, 18 Jun 2020 08:43:06 +0200 Christoph Hellwig <hch@lst.de> wrote:
> > > 
> > > > Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> > > > page read-only just after the allocation.
> > > > 
> > > > --- a/arch/arm64/kernel/probes/kprobes.c
> > > > +++ b/arch/arm64/kernel/probes/kprobes.c
> > > > @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
> > > >  
> > > >  void *alloc_insn_page(void)
> > > >  {
> > > > -	void *page;
> > > > -
> > > > -	page = vmalloc_exec(PAGE_SIZE);
> > > > -	if (page) {
> > > > -		set_memory_ro((unsigned long)page, 1);
> > > > -		set_vm_flush_reset_perms(page);
> > > > -	}
> > > > -
> > > > -	return page;
> > > > +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > > > +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > > > +			NUMA_NO_NODE, __func__);
> > > >  }
> > > >  
> > > >  /* arm kprobe: install breakpoint in text */
> > > 
> > > But why.  I think this is just a cleanup, doesn't address any runtime issue?
> > 
> > It doesn't "fix" an issue - it just simplifies and speeds up the code.
> 
> Ok, but I don't understand the PLT comment from Peter in
> 20200618092754.GF576905@hirez.programming.kicks-ass.net:
> 
>   | I think this has the exact same range issue as the x86 user. But it
>   | might be less fatal if their PLT magic can cover the full range.
> 
> Peter, please could you elaborate on your concern? I feel like I'm missing
> some context.

On x86 we can only directly call code in a (signed) 32bit immediate
range (2G) and our kernel text and module range are constrained by that.

IIRC ARM64 has an even smaller immediate range and needs to play fixup
games with trampolines or somesuch (there was an ARM specific name for
it that I've misplaced again). Does that machinery cover the entire
vmalloc space or are you only able to fix up for a smaller range?

Your arch/arm64/kernel/module.c:module_alloc() implementation seems to
have an explicit module range different from the full vmalloc range, I'm
thinking this is for a reason.
Will Deacon June 23, 2020, 9:57 a.m. UTC | #9
On Tue, Jun 23, 2020 at 11:37:14AM +0200, Peter Zijlstra wrote:
> On Tue, Jun 23, 2020 at 10:07:58AM +0100, Will Deacon wrote:
> > On Tue, Jun 23, 2020 at 11:05:05AM +0200, Christoph Hellwig wrote:
> > > On Sat, Jun 20, 2020 at 07:16:16PM -0700, Andrew Morton wrote:
> > > > On Thu, 18 Jun 2020 08:43:06 +0200 Christoph Hellwig <hch@lst.de> wrote:
> > > > > --- a/arch/arm64/kernel/probes/kprobes.c
> > > > > +++ b/arch/arm64/kernel/probes/kprobes.c
> > > > > @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
> > > > >  
> > > > >  void *alloc_insn_page(void)
> > > > >  {
> > > > > -	void *page;
> > > > > -
> > > > > -	page = vmalloc_exec(PAGE_SIZE);
> > > > > -	if (page) {
> > > > > -		set_memory_ro((unsigned long)page, 1);
> > > > > -		set_vm_flush_reset_perms(page);
> > > > > -	}
> > > > > -
> > > > > -	return page;
> > > > > +	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > > > > +			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > > > > +			NUMA_NO_NODE, __func__);
> > > > >  }
> > > > >  
> > > > >  /* arm kprobe: install breakpoint in text */
> > > > 
> > > > But why.  I think this is just a cleanup, doesn't address any runtime issue?
> > > 
> > > It doesn't "fix" an issue - it just simplifies and speeds up the code.
> > 
> > Ok, but I don't understand the PLT comment from Peter in
> > 20200618092754.GF576905@hirez.programming.kicks-ass.net:
> > 
> >   | I think this has the exact same range issue as the x86 user. But it
> >   | might be less fatal if their PLT magic can cover the full range.
> > 
> > Peter, please could you elaborate on your concern? I feel like I'm missing
> > some context.
> 
> On x86 we can only directly call code in a (signed) 32bit immediate
> range (2G) and our kernel text and module range are constrained by that.
> 
> IIRC ARM64 has an even smaller immediate range and needs to play fixup
> games with trampolines or somesuch (there was an ARM specific name for
> it that I've misplaced again). Does that machinery cover the entire
> vmalloc space or are you only able to fix up for a smaller range?
> 
> Your arch/arm64/kernel/module.c:module_alloc() implementation seems to
> have an explicit module range different from the full vmalloc range, I'm
> thinking this is for a reason.

Ah, gotcha. In this case, we're talking about the kprobe out-of-line
buffer. We don't directly branch to that; instead we take a BRK exception
and either exception return + singlestep the OOL buffer, or we simulate
the instruction if it's doing anything PC-relative, so I don't see the
need for a PLT.

Will
Ard Biesheuvel June 27, 2020, 7:34 a.m. UTC | #10
On Thu, 18 Jun 2020 at 08:44, Christoph Hellwig <hch@lst.de> wrote:
>
> Use PAGE_KERNEL_ROX directly instead of allocating RWX and setting the
> page read-only just after the allocation.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  arch/arm64/kernel/probes/kprobes.c | 12 +++---------
>  1 file changed, 3 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> index d1c95dcf1d7833..cbe49cd117cfec 100644
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -120,15 +120,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>
>  void *alloc_insn_page(void)
>  {
> -       void *page;
> -
> -       page = vmalloc_exec(PAGE_SIZE);
> -       if (page) {
> -               set_memory_ro((unsigned long)page, 1);
> -               set_vm_flush_reset_perms(page);
> -       }
> -
> -       return page;
> +       return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> +                       GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> +                       NUMA_NO_NODE, __func__);

Why is this passing a string for the 'caller' argument, and not the
address of the caller?


>  }
>
>  /* arm kprobe: install breakpoint in text */
> --
> 2.26.2
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Christoph Hellwig June 27, 2020, 7:56 a.m. UTC | #11
On Sat, Jun 27, 2020 at 09:34:42AM +0200, Ard Biesheuvel wrote:
> > +       return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > +                       GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > +                       NUMA_NO_NODE, __func__);
> 
> Why is this passing a string for the 'caller' argument, and not the
> address of the caller?

Dementia.  Fix sent.
Ard Biesheuvel June 27, 2020, 7:57 a.m. UTC | #12
On Sat, 27 Jun 2020 at 09:57, Christoph Hellwig <hch@lst.de> wrote:
>
> On Sat, Jun 27, 2020 at 09:34:42AM +0200, Ard Biesheuvel wrote:
> > > +       return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
> > > +                       GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
> > > +                       NUMA_NO_NODE, __func__);
> >
> > Why is this passing a string for the 'caller' argument, and not the
> > address of the caller?
>
> Dementia.  Fix sent.

:-)
diff mbox series

Patch

diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index d1c95dcf1d7833..cbe49cd117cfec 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -120,15 +120,9 @@  int __kprobes arch_prepare_kprobe(struct kprobe *p)
 
 void *alloc_insn_page(void)
 {
-	void *page;
-
-	page = vmalloc_exec(PAGE_SIZE);
-	if (page) {
-		set_memory_ro((unsigned long)page, 1);
-		set_vm_flush_reset_perms(page);
-	}
-
-	return page;
+	return __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END,
+			GFP_KERNEL, PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS,
+			NUMA_NO_NODE, __func__);
 }
 
 /* arm kprobe: install breakpoint in text */