diff mbox

[v2,10/10] ARM: software-based priviledged-no-access support

Message ID 20151009112418.GN32532@n2100.arm.linux.org.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Russell King - ARM Linux Oct. 9, 2015, 11:24 a.m. UTC
On Fri, Oct 09, 2015 at 11:53:09AM +0100, Will Deacon wrote:
> On Fri, Oct 09, 2015 at 10:28:14AM +0200, Linus Walleij wrote:
> > On Tue, Aug 25, 2015 at 5:42 PM, Russell King
> > <rmk+kernel@arm.linux.org.uk> wrote:
> > 
> > > Provide a software-based implementation of the priviledged no access
> > > support found in ARMv8.1.
> > >
> > > Userspace pages are mapped using a different domain number from the
> > > kernel and IO mappings.  If we switch the user domain to "no access"
> > > when we enter the kernel, we can prevent the kernel from touching
> > > userspace.
> > >
> > > However, the kernel needs to be able to access userspace via the
> > > various user accessor functions.  With the wrapping in the previous
> > > patch, we can temporarily enable access when the kernel needs user
> > > access, and re-disable it afterwards.
> > >
> > > This allows us to trap non-intended accesses to userspace, eg, caused
> > > by an inadvertent dereference of the LIST_POISON* values, which, with
> > > appropriate user mappings setup, can be made to succeed.  This in turn
> > > can allow use-after-free bugs to be further exploited than would
> > > otherwise be possible.
> > >
> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > 
> > For some reason this patch explodes on my ARM PB11MPCore, it
> > is a weird beast and corner case machine so I guess that is why
> > it wasn't noticed. This happens a bit into the boot when freeing
> > unused pages:
> > 
> > Freeing unused kernel memory: 2672K (c0448000 - c06e4000)
> > Unable to handle kernel paging request at virtual address b6f069f4
> > pgd = c6e58000
> > [b6f069f4] *pgd=76e09831, *pte=77ff759f, *ppte=77ff7e6e
> > Internal error: Oops: 17 [#1] SMP ARM
> > Modules linked in:
> > CPU: 2 PID: 1 Comm: init Not tainted 4.3.0-rc4-00015-gf6702681a0af #48
> > Hardware name: ARM-RealView PB11MPCore
> > task: c7827bc0 ti: c782c000 task.ti: c782c000
> > PC is at v6wbi_flush_user_tlb_range+0x28/0x48
> > LR is at on_each_cpu_mask+0x58/0x60
> > pc : [<c001abf0>]    lr : [<c007c18c>]    psr: 20000093
> > sp : c782deb8  ip : 00000000  fp : 00000000
> > r10: c6e5adc8  r9 : 00000001  r8 : b6f02000
> > r7 : c7a17180  r6 : c782ded4  r5 : c0015118  r4 : 20000013
> > r3 : 00000002  r2 : 00100075  r1 : b6f02000  r0 : b6f01002
> > Flags: nzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > Control: 00c5787d  Table: 76e5800a  DAC: 00000051
> 
> It looks like we're faulting on the TLBI instruction, because it's
> targetting a userspace address (r0 == 0xb6f01002) and the DAC prohibits
> access to userspace.

That's kind'a weird - I'd have expected _cache_ operations to fault,
but not TLB operations.  From a quick search of the ARM ARM, I can't
find anything that says that TLB operations are allowed to fault.

> It's weird that this only seems to happen on 11MPCore
> though; if this core was one of the guys getting cross-called, then I
> could understand the bug, but the lr suggests that CPU 2 is initiating
> the flush, so I'd expect the same problem to appear on any ARMv6 part.

It sounds to me like a CPU bug, but one which we need to work around.
ipi_flush_tlb_range() will be the function concerned, we need to
save-and-enable, and then restore the user access state around that
call.

> Russell, have you tried the s/w PAN stuff on any v6 CPUs?

No.  I have considered having the Realview EB board as part of the test
farm, but as that board is hassle to get going, I deem the hardware to
be too unreliable for that.  (I reported the problem at the time.)

Linus, can you try the patch below to see if it resolves the problem
you're seeing please?

 arch/arm/kernel/smp_tlb.c | 7 +++++++
 1 file changed, 7 insertions(+)

Comments

Will Deacon Oct. 9, 2015, 12:32 p.m. UTC | #1
On Fri, Oct 09, 2015 at 12:24:18PM +0100, Russell King - ARM Linux wrote:
> On Fri, Oct 09, 2015 at 11:53:09AM +0100, Will Deacon wrote:
> > It looks like we're faulting on the TLBI instruction, because it's
> > targetting a userspace address (r0 == 0xb6f01002) and the DAC prohibits
> > access to userspace.
> 
> That's kind'a weird - I'd have expected _cache_ operations to fault,
> but not TLB operations.  From a quick search of the ARM ARM, I can't
> find anything that says that TLB operations are allowed to fault.

Yes, you're right. I was concentrating on the cross-call scenario and
forgot this was TLB not cache maintenance, so faulting is indeed bizarre.

> > It's weird that this only seems to happen on 11MPCore
> > though; if this core was one of the guys getting cross-called, then I
> > could understand the bug, but the lr suggests that CPU 2 is initiating
> > the flush, so I'd expect the same problem to appear on any ARMv6 part.
> 
> It sounds to me like a CPU bug, but one which we need to work around.
> ipi_flush_tlb_range() will be the function concerned, we need to
> save-and-enable, and then restore the user access state around that
> call.
> 
> > Russell, have you tried the s/w PAN stuff on any v6 CPUs?
> 
> No.  I have considered having the Realview EB board as part of the test
> farm, but as that board is hassle to get going, I deem the hardware to
> be too unreliable for that.  (I reported the problem at the time.)
> 
> Linus, can you try the patch below to see if it resolves the problem
> you're seeing please?

If this fixes things, I'll dig up the errata document to see if this is
a known hardware issue.

Will
Linus Walleij Oct. 12, 2015, 7:51 a.m. UTC | #2
On Fri, Oct 9, 2015 at 1:24 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Linus, can you try the patch below to see if it resolves the problem
> you're seeing please?
>
>  arch/arm/kernel/smp_tlb.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
> index 2e72be4f623e..7cb079e74010 100644
> --- a/arch/arm/kernel/smp_tlb.c
> +++ b/arch/arm/kernel/smp_tlb.c
> @@ -9,6 +9,7 @@
>   */
>  #include <linux/preempt.h>
>  #include <linux/smp.h>
> +#include <linux/uaccess.h>
>
>  #include <asm/smp_plat.h>
>  #include <asm/tlbflush.h>
> @@ -40,8 +41,11 @@ static inline void ipi_flush_tlb_mm(void *arg)
>  static inline void ipi_flush_tlb_page(void *arg)
>  {
>         struct tlb_args *ta = (struct tlb_args *)arg;
> +       unsigned int __ua_flags = uaccess_save_and_enable();
>
>         local_flush_tlb_page(ta->ta_vma, ta->ta_start);
> +
> +       uaccess_restore(__ua_flags);
>  }
>
>  static inline void ipi_flush_tlb_kernel_page(void *arg)
> @@ -54,8 +58,11 @@ static inline void ipi_flush_tlb_kernel_page(void *arg)
>  static inline void ipi_flush_tlb_range(void *arg)
>  {
>         struct tlb_args *ta = (struct tlb_args *)arg;
> +       unsigned int __ua_flags = uaccess_save_and_enable();
>
>         local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
> +
> +       uaccess_restore(__ua_flags);
>  }

Yes this makes it rock solid again.
Tested-by: Linus Walleij <linus.walleij@linaro.org>

I guess it will only affect ARMv6 SMP (11MPCore) machines and there
are not so many of them AFAICT, so I guess it's a bit annoying to have
that in the ipi_flush_tlb_kernel_page() for all of the ARM CPUs :(
but it does work.

Yours,
Linus Walleij
Linus Walleij Oct. 23, 2015, 8:05 a.m. UTC | #3
On Fri, Oct 9, 2015 at 1:24 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
>  [Will]
>> It's weird that this only seems to happen on 11MPCore
>> though; if this core was one of the guys getting cross-called, then I
>> could understand the bug, but the lr suggests that CPU 2 is initiating
>> the flush, so I'd expect the same problem to appear on any ARMv6 part.
>
> It sounds to me like a CPU bug, but one which we need to work around.
> ipi_flush_tlb_range() will be the function concerned, we need to
> save-and-enable, and then restore the user access state around that
> call.
>
>> Russell, have you tried the s/w PAN stuff on any v6 CPUs?
>
> No.  I have considered having the Realview EB board as part of the test
> farm, but as that board is hassle to get going, I deem the hardware to
> be too unreliable for that.  (I reported the problem at the time.)
>
> Linus, can you try the patch below to see if it resolves the problem
> you're seeing please?
>
>  arch/arm/kernel/smp_tlb.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
> index 2e72be4f623e..7cb079e74010 100644
> --- a/arch/arm/kernel/smp_tlb.c
> +++ b/arch/arm/kernel/smp_tlb.c
> @@ -9,6 +9,7 @@
>   */
>  #include <linux/preempt.h>
>  #include <linux/smp.h>
> +#include <linux/uaccess.h>
>
>  #include <asm/smp_plat.h>
>  #include <asm/tlbflush.h>
> @@ -40,8 +41,11 @@ static inline void ipi_flush_tlb_mm(void *arg)
>  static inline void ipi_flush_tlb_page(void *arg)
>  {
>         struct tlb_args *ta = (struct tlb_args *)arg;
> +       unsigned int __ua_flags = uaccess_save_and_enable();
>
>         local_flush_tlb_page(ta->ta_vma, ta->ta_start);
> +
> +       uaccess_restore(__ua_flags);
>  }
>
>  static inline void ipi_flush_tlb_kernel_page(void *arg)
> @@ -54,8 +58,11 @@ static inline void ipi_flush_tlb_kernel_page(void *arg)
>  static inline void ipi_flush_tlb_range(void *arg)
>  {
>         struct tlb_args *ta = (struct tlb_args *)arg;
> +       unsigned int __ua_flags = uaccess_save_and_enable();
>
>         local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
> +
> +       uaccess_restore(__ua_flags);
>  }
>
>  static inline void ipi_flush_tlb_kernel_range(void *arg)

Do we have a solution for this?

I'm carrying the patch and v4.3-rc6 is broken on upstream
RealView PB11MPCore, at least for me. :(

Yours,
Linus Walleij
Russell King - ARM Linux Oct. 23, 2015, 8:46 a.m. UTC | #4
On Fri, Oct 23, 2015 at 10:05:06AM +0200, Linus Walleij wrote:
> Do we have a solution for this?
> 
> I'm carrying the patch and v4.3-rc6 is broken on upstream
> RealView PB11MPCore, at least for me. :(

Will was going to do some research and get back to me, but what he was
going to look at has been archived off-site.  Will said he wanted to
reproduce it on the latest silicon, but I haven't heard anything
further and he hasn't been around this week.
Will Deacon Oct. 27, 2015, 5:11 p.m. UTC | #5
On Fri, Oct 23, 2015 at 09:46:40AM +0100, Russell King - ARM Linux wrote:
> On Fri, Oct 23, 2015 at 10:05:06AM +0200, Linus Walleij wrote:
> > Do we have a solution for this?
> > 
> > I'm carrying the patch and v4.3-rc6 is broken on upstream
> > RealView PB11MPCore, at least for me. :(
> 
> Will was going to do some research and get back to me, but what he was
> going to look at has been archived off-site.  Will said he wanted to
> reproduce it on the latest silicon, but I haven't heard anything
> further and he hasn't been around this week.

I'm afraid my digging didn't prove to be very enlightening. One thing I'd
like to try, is to see if we can reproduce the problem on an 11MPCore
platform with a CPU > r0p0. However, I don't have access to such a platform
that can run mainline, so without that I think we have to go with Russell's
workaround predicated on a check for 11MPCore.

Will
diff mbox

Patch

diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index 2e72be4f623e..7cb079e74010 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -9,6 +9,7 @@ 
  */
 #include <linux/preempt.h>
 #include <linux/smp.h>
+#include <linux/uaccess.h>
 
 #include <asm/smp_plat.h>
 #include <asm/tlbflush.h>
@@ -40,8 +41,11 @@  static inline void ipi_flush_tlb_mm(void *arg)
 static inline void ipi_flush_tlb_page(void *arg)
 {
 	struct tlb_args *ta = (struct tlb_args *)arg;
+	unsigned int __ua_flags = uaccess_save_and_enable();
 
 	local_flush_tlb_page(ta->ta_vma, ta->ta_start);
+
+	uaccess_restore(__ua_flags);
 }
 
 static inline void ipi_flush_tlb_kernel_page(void *arg)
@@ -54,8 +58,11 @@  static inline void ipi_flush_tlb_kernel_page(void *arg)
 static inline void ipi_flush_tlb_range(void *arg)
 {
 	struct tlb_args *ta = (struct tlb_args *)arg;
+	unsigned int __ua_flags = uaccess_save_and_enable();
 
 	local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
+
+	uaccess_restore(__ua_flags);
 }
 
 static inline void ipi_flush_tlb_kernel_range(void *arg)