This patchset implements the asynchronous mode support for ARMv8.5-A
Memory Tagging Extension (MTE), which is a debugging feature that allows
to detect with the help of the architecture the C and C++ programmatic
memory errors like buffer overflow, use-after-free, use-after-return, etc.
MTE is built on top of the AArch64 v8.0 virtual address tagging TBI
(Top Byte Ignore) feature and allows a task to set a 4 bit tag on any
subset of its address space that is multiple of a 16 bytes granule. MTE
is based on a lock-key mechanism where the lock is the tag associated to
the physical memory and the key is the tag associated to the virtual
address.
When MTE is enabled and tags are set for ranges of address space of a task,
the PE will compare the tag related to the physical memory with the tag
related to the virtual address (tag check operation). Access to the memory
is granted only if the two tags match. In case of mismatch the PE will raise
an exception.
The exception can be handled synchronously or asynchronously. When the
asynchronous mode is enabled:
- Upon fault the PE updates the TFSR_EL1 register.
- The kernel detects the change during one of the following:
- Context switching
- Return to user/EL0
- Kernel entry from EL1
- Kernel exit to EL1
- If the register has been updated by the PE the kernel clears it and
reports the error.
The series contains as well an optimization to mte_assign_mem_tag_range().
The series is based on linux 5.11-rc3.
To simplify the testing a tree with the new patches on top has been made
available at [1].
[1] https://git.gitlab.arm.com/linux-arm/linux-vf.git mte/v10.async
Changes:
--------
v3:
- Exposed kasan_hw_tags_mode to convert the internal
KASAN represenetation.
- Added dsb() for kernel exit paths in arm64.
- Addressed review comments.
v2:
- Fixed a compilation issue reported by krobot.
- General cleanup.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Andrey Ryabinin <redacted>
Cc: Alexander Potapenko <glider@google.com>
Cc: Marco Elver <elver@google.com>
Cc: Evgenii Stepanov <redacted>
Cc: Branislav Rankov <redacted>
Cc: Andrey Konovalov <redacted>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Vincenzo Frascino (4):
kasan, arm64: Add KASAN light mode
arm64: mte: Add asynchronous mode support
arm64: mte: Enable async tag check fault
arm64: mte: Optimize mte_assign_mem_tag_range()
arch/arm64/include/asm/memory.h | 2 +-
arch/arm64/include/asm/mte-kasan.h | 5 ++-
arch/arm64/include/asm/mte.h | 47 +++++++++++++++++++++-
arch/arm64/kernel/entry-common.c | 11 ++++++
arch/arm64/kernel/mte.c | 63 ++++++++++++++++++++++++++++--
arch/arm64/lib/mte.S | 15 -------
include/linux/kasan.h | 1 +
include/linux/kasan_def.h | 10 +++++
mm/kasan/hw_tags.c | 19 ++++++++-
mm/kasan/kasan.h | 2 +-
10 files changed, 151 insertions(+), 24 deletions(-)
create mode 100644 include/linux/kasan_def.h
--
2.30.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Architectures supported by KASAN HW can provide a light mode of
execution. On an MTE enabled arm64 hw for example this can be identified
with the asynch mode of execution.
In this mode, if a tag check fault occurs, the TFSR_EL1 register is
updated asynchronously. The kernel checks the corresponding bits
periodically.
KASAN requires a specific mode of execution to make use of this hw feature.
Add KASAN HW light execution mode.
Note: This patch adds the KASAN_ARG_MODE_LIGHT config option and the
"light" kernel command line option to enable the described feature.
This patch introduces the kasan_def.h header to make easier to propagate
the relevant enumerations to the architectural code.
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Andrey Ryabinin <redacted>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <redacted>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/include/asm/memory.h | 2 +-
arch/arm64/include/asm/mte-kasan.h | 5 +++--
arch/arm64/kernel/mte.c | 2 +-
include/linux/kasan.h | 1 +
include/linux/kasan_def.h | 10 ++++++++++
mm/kasan/hw_tags.c | 19 ++++++++++++++++++-
mm/kasan/kasan.h | 2 +-
7 files changed, 35 insertions(+), 6 deletions(-)
create mode 100644 include/linux/kasan_def.h
@@ -60,6 +61,8 @@ static int __init early_kasan_mode(char *arg)if(!strcmp(arg,"off"))kasan_arg_mode=KASAN_ARG_MODE_OFF;+elseif(!strcmp(arg,"light"))+kasan_arg_mode=KASAN_ARG_MODE_LIGHT;elseif(!strcmp(arg,"prod"))kasan_arg_mode=KASAN_ARG_MODE_PROD;elseif(!strcmp(arg,"full"))
@@ -105,9 +108,21 @@ static int __init early_kasan_fault(char *arg)}early_param("kasan.fault",early_kasan_fault);+staticinlineinthw_init_mode(enumkasan_arg_modemode)+{+switch(mode){+caseKASAN_ARG_MODE_LIGHT:+returnKASAN_HW_TAGS_ASYNC;+default:+returnKASAN_HW_TAGS_SYNC;+}+}+/* kasan_init_hw_tags_cpu() is called for each CPU. */voidkasan_init_hw_tags_cpu(void){+enumkasan_hw_tags_modehw_mode;+/**There'snoneedtocheckthatthehardwareisMTE-capablehere,*asthisfunctionisonlycalledforMTE-capablehardware.
@@ -118,7 +133,8 @@ void kasan_init_hw_tags_cpu(void)return;hw_init_tags(KASAN_TAG_MAX);-hw_enable_tagging();+hw_mode=hw_init_mode(kasan_arg_mode);+hw_enable_tagging(hw_mode);}/* kasan_init_hw_tags() is called once on boot CPU. */
@@ -145,6 +161,7 @@ void __init kasan_init_hw_tags(void)caseKASAN_ARG_MODE_OFF:/* If KASAN is disabled, do nothing. */return;+caseKASAN_ARG_MODE_LIGHT:caseKASAN_ARG_MODE_PROD:static_branch_enable(&kasan_flag_enabled);break;
MTE provides an asynchronous mode for detecting tag exceptions. In
particular instead of triggering a fault the arm64 core updates a
register which is checked by the kernel after the asynchronous tag
check fault has occurred.
Add support for MTE asynchronous mode.
The exception handling mechanism will be added with a future patch.
Note: KASAN HW activates async mode via kasan.mode kernel parameter.
The default mode is set to synchronous.
The code that verifies the status of TFSR_EL1 will be added with a
future patch.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/kernel/mte.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
@@ -153,8 +153,30 @@ void mte_init_tags(u64 max_tag)voidmte_enable_kernel(enumkasan_hw_tags_modemode){-/* Enable MTE Sync Mode for EL1. */-sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_SYNC);+constchar*m;++/* Preset parameter values based on the mode. */+switch(mode){+caseKASAN_HW_TAGS_ASYNC:+/* Enable MTE Async Mode for EL1. */+sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_ASYNC);+m="asynchronous";+break;+caseKASAN_HW_TAGS_SYNC:+sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_SYNC);+m="synchronous";+break;+default:+/*+*kasanmodeshouldbealwayssethenceweshould+*notreachthiscondition.+*/+WARN_ON_ONCE(1);+return;+}++pr_info_once("MTE: enabled in %s mode at EL1\n",m);+isb();}
--
2.30.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
MTE provides a mode that asynchronously updates the TFSR_EL1 register
when a tag check exception is detected.
To take advantage of this mode the kernel has to verify the status of
the register at:
1. Context switching
2. Return to user/EL0 (Not required in entry from EL0 since the kernel
did not run)
3. Kernel entry from EL1
4. Kernel exit to EL1
If the register is non-zero a trace is reported.
Add the required features for EL1 detection and reporting.
Note: ITFSB bit is set in the SCTLR_EL1 register hence it guaranties that
the indirect writes to TFSR_EL1 are synchronized at exception entry to
EL1. On the context switch path the synchronization is guarantied by the
dsb() in __switch_to().
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/include/asm/mte.h | 21 +++++++++++++++++++
arch/arm64/kernel/entry-common.c | 11 ++++++++++
arch/arm64/kernel/mte.c | 35 ++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
mte_assign_mem_tag_range() is called on production KASAN HW hot
paths. It makes sense to optimize it in an attempt to reduce the
overhead.
Optimize mte_assign_mem_tag_range() based on the indications provided at
[1].
[1] https://lore.kernel.org/r/CAAeHK+wCO+J7D1_T89DG+jJrPLk3X9RsGFKxJGd0ZcUFjQT-9Q@mail.gmail.com/
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/include/asm/mte.h | 26 +++++++++++++++++++++++++-
arch/arm64/lib/mte.S | 15 ---------------
2 files changed, 25 insertions(+), 16 deletions(-)
From: Mark Rutland <mark.rutland@arm.com> Date: 2021-01-15 15:09:27
On Fri, Jan 15, 2021 at 12:00:40PM +0000, Vincenzo Frascino wrote:
Architectures supported by KASAN HW can provide a light mode of
execution. On an MTE enabled arm64 hw for example this can be identified
with the asynch mode of execution.
In this mode, if a tag check fault occurs, the TFSR_EL1 register is
updated asynchronously. The kernel checks the corresponding bits
periodically.
What's the expected usage of this relative to prod, given that this has
to be chosen at boot time? When/where is this expected to be used
relative to prod mode?
Rather than passing a mode in, I think it'd be better to have:
* arch_enable_tagging_prod()
* arch_enable_tagging_light()
... that we can map in the arch code to separate:
* mte_enable_kernel_sync()
* mte_enable_kernel_async()
... as by construction that avoids calls with an unhandled mode, and we
wouldn't need the mode enum kasan_hw_tags_mode...
... and we can just have a wrapper like this to call either of the two functions directly, i.e.
static inline void hw_enable_tagging_mode(enum kasan_arg_mode mode)
{
if (mode == KASAN_ARG_MODE_LIGHT)
arch_enable_tagging_mode_light();
else
arch_enable_tagging_mode_prod();
}
Thanks,
Mark.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Mark Rutland <mark.rutland@arm.com> Date: 2021-01-15 15:14:30
On Fri, Jan 15, 2021 at 12:00:41PM +0000, Vincenzo Frascino wrote:
quoted hunk
MTE provides an asynchronous mode for detecting tag exceptions. In
particular instead of triggering a fault the arm64 core updates a
register which is checked by the kernel after the asynchronous tag
check fault has occurred.
Add support for MTE asynchronous mode.
The exception handling mechanism will be added with a future patch.
Note: KASAN HW activates async mode via kasan.mode kernel parameter.
The default mode is set to synchronous.
The code that verifies the status of TFSR_EL1 will be added with a
future patch.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/kernel/mte.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
@@ -153,8 +153,30 @@ void mte_init_tags(u64 max_tag)voidmte_enable_kernel(enumkasan_hw_tags_modemode){-/* Enable MTE Sync Mode for EL1. */-sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_SYNC);+constchar*m;++/* Preset parameter values based on the mode. */+switch(mode){+caseKASAN_HW_TAGS_ASYNC:+/* Enable MTE Async Mode for EL1. */+sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_ASYNC);+m="asynchronous";+break;+caseKASAN_HW_TAGS_SYNC:+sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_SYNC);+m="synchronous";+break;+default:+/*+*kasanmodeshouldbealwayssethenceweshould+*notreachthiscondition.+*/+WARN_ON_ONCE(1);+return;+}++pr_info_once("MTE: enabled in %s mode at EL1\n",m);+isb();}
For clarity, we should have that ISB before the pr_info_once().
As with my comment on patch 1, I think with separate functions this
would be much clearer and simpler:
static inline void __mte_enable_kernel(const char *mode, unsigned long tcf)
{
sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, tcf);
isb();
pr_info_once("MTE: enabled in %s mode at EL1\n", mode);
}
void mte_enable_kernel_sync(void)
{
__mte_enable_kernel("synchronous", SCTLR_ELx_TCF_SYNC);
}
void mte_enable_kernel_async(void)
{
__mte_enable_kernel("asynchronous", SCTLR_ELx_TCF_ASYNC);
}
Thanks,
Mark.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Mark Rutland <mark.rutland@arm.com> Date: 2021-01-15 15:39:01
On Fri, Jan 15, 2021 at 12:00:42PM +0000, Vincenzo Frascino wrote:
quoted hunk
MTE provides a mode that asynchronously updates the TFSR_EL1 register
when a tag check exception is detected.
To take advantage of this mode the kernel has to verify the status of
the register at:
1. Context switching
2. Return to user/EL0 (Not required in entry from EL0 since the kernel
did not run)
3. Kernel entry from EL1
4. Kernel exit to EL1
If the register is non-zero a trace is reported.
Add the required features for EL1 detection and reporting.
Note: ITFSB bit is set in the SCTLR_EL1 register hence it guaranties that
the indirect writes to TFSR_EL1 are synchronized at exception entry to
EL1. On the context switch path the synchronization is guarantied by the
dsb() in __switch_to().
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/include/asm/mte.h | 21 +++++++++++++++++++
arch/arm64/kernel/entry-common.c | 11 ++++++++++
arch/arm64/kernel/mte.c | 35 ++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
Nit: can we please use "sync" rather than "synch", to match what we do
elsewhere, e.g. mte_check_tfsr_el1_no_sync immediately above. The
inconsistency is unfortunate and distracting.
+ * TFSR_EL1 on kernel entry but for exit an explicit dsb()
+ * is required.
+ */
+ dsb(ish);
+}
Did you mean to have the barrier /before/ checking the TFSR? I'm
confused as to why it's after the check if the point of it is to ensure
that TFSR has been updated.
I don't understand this difference between the entry/exit paths; are you
relying on a prior DSB in the entry path?
Is the DSB alone sufficient to update the TFSR (i.e. is an indirect
write ordered before a direct read)? ... or do you need a DSB + ISB
here?
It's probably worth a comment as to why the ISH domain is correct here
rather than NSH or SY. I'm not entirely certain if ISH is necessary or
sufficient, but it depends on the completion rules.
[...]
quoted hunk
quoted
/*
@@ -47,6 +49,13 @@ static void noinstr exit_to_kernel_mode(struct pt_regs *regs) { lockdep_assert_irqs_disabled();+ /*+ * The dsb() in mte_check_tfsr_el1() is required to relate+ * the asynchronous tag check fault to the context in which+ * it happens.+ */+ mte_check_tfsr_el1();
I think this comment is misplaced, given that mte_check_tfsr_el1() isn't
even in the same file.
If you need to do different things upon entry/exit, I'd rather we had
separate functions, e.g.
* mte_check_tfsr_entry();
* mte_check_tfsr_exit();
... since then it's immediately obvious in context as to whether we're
using the right function, and then we can have a comment within each of
the functions explaining what we need to do in that specific case.
quoted hunk
if (interrupts_enabled(regs)) {
if (regs->exit_rcu) {
trace_hardirqs_on_prepare();
I thing it's worth spelling out what TF0 == 1 means, e.g.
/*
* The kernel should never trigger an asynchronous fault on a
* TTBR0 address, so we should never see TF0 set.
* For futexes we disable checks via PSTATE.TCO.
*/
... what about regular uaccess using LDTR/STTR? What happens for those?
+ WARN_ON(tfsr_el1 & SYS_TFSR_EL1_TF0);
It's probably worth giving this a message so that we can debug it more
easily, e.g.
WARN(tfsr_el1 & SYS_TFSR_EL1_TF0,
"Kernel async tag fault on TTBR0 address");
+ if (tfsr_el1 & SYS_TFSR_EL1_TF1) {
It might be worth wrapping this with an unlikely(), given we hope this
never happens.
Thanks,
Mark.
@@ -245,6 +271,15 @@ void mte_thread_switch(struct task_struct *next) /* avoid expensive SCTLR_EL1 accesses if no change */ if (current->thread.sctlr_tcf0 != next->thread.sctlr_tcf0) update_sctlr_el1_tcf0(next->thread.sctlr_tcf0);++ /*+ * Check if an async tag exception occurred at EL1.+ *+ * Note: On the context switch path we rely on the dsb() present+ * in __switch_to() to guarantee that the indirect writes to TFSR_EL1+ * are synchronized before this point.+ */+ mte_check_tfsr_el1_no_sync(); } void mte_suspend_exit(void)
From: Mark Rutland <mark.rutland@arm.com> Date: 2021-01-15 15:46:17
On Fri, Jan 15, 2021 at 12:00:43PM +0000, Vincenzo Frascino wrote:
mte_assign_mem_tag_range() is called on production KASAN HW hot
paths. It makes sense to optimize it in an attempt to reduce the
overhead.
Optimize mte_assign_mem_tag_range() based on the indications provided at
[1].
... what exactly is the optimization?
I /think/ you're just trying to have it inlined, but you should mention
that explicitly.
@@ -49,7 +49,31 @@ long get_mte_ctrl(struct task_struct *task);intmte_ptrace_copy_tags(structtask_struct*child,longrequest,unsignedlongaddr,unsignedlongdata);-voidmte_assign_mem_tag_range(void*addr,size_tsize);+staticinlinevoidmte_assign_mem_tag_range(void*addr,size_tsize)+{+u64_addr=(u64)addr;+u64_end=_addr+size;++/*+*ThisfunctionmustbeinvokedfromanMTEenabledcontext.+*+*Note:Theaddressmustbenon-NULLandMTE_GRANULE_SIZEalignedand+*sizemustbenon-zeroandMTE_GRANULE_SIZEaligned.+*/+do{+/*+*'asmvolatile'isrequiredtopreventthecompilertomove+*thestatementoutsideoftheloop.+*/+asmvolatile(__MTE_PREAMBLE"stg %0, [%0]"+:+:"r"(_addr)+:"memory");++_addr+=MTE_GRANULE_SIZE;+}while(_addr<_end);
Is there any chance that this can be used for the last bytes of the
virtual address space? This might need to change to `_addr == _end` if
that is possible, otherwise it'll terminate early in that case.
+}
What does the code generation look like for this, relative to the
assembly version?
Thanks,
Mark.
On Fri, Jan 15, 2021 at 1:00 PM Vincenzo Frascino
[off-list ref] wrote:
quoted hunk
Architectures supported by KASAN HW can provide a light mode of
execution. On an MTE enabled arm64 hw for example this can be identified
with the asynch mode of execution.
In this mode, if a tag check fault occurs, the TFSR_EL1 register is
updated asynchronously. The kernel checks the corresponding bits
periodically.
KASAN requires a specific mode of execution to make use of this hw feature.
Add KASAN HW light execution mode.
Note: This patch adds the KASAN_ARG_MODE_LIGHT config option and the
"light" kernel command line option to enable the described feature.
This patch introduces the kasan_def.h header to make easier to propagate
the relevant enumerations to the architectural code.
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Andrey Ryabinin <redacted>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <redacted>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/include/asm/memory.h | 2 +-
arch/arm64/include/asm/mte-kasan.h | 5 +++--
arch/arm64/kernel/mte.c | 2 +-
include/linux/kasan.h | 1 +
include/linux/kasan_def.h | 10 ++++++++++
mm/kasan/hw_tags.c | 19 ++++++++++++++++++-
mm/kasan/kasan.h | 2 +-
7 files changed, 35 insertions(+), 6 deletions(-)
create mode 100644 include/linux/kasan_def.h
Thanks for this. I will have a look into it today. In the meantime, could you
please elaborate a bit more on kasan.trap?
That's what I call the boot parameter that allows switching between
sync and async. We'll need one as we're dropping
kasan.mode=off/prod/light/full.
Feel free to name it differently. Perhaps, as kasan.mode is now
unused, we can use that for sync/async.
I see, thanks for the explanation. "mode" or "trap" would work for me.
--
Regards,
Vincenzo
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hi Andrey,
On 1/15/21 6:59 PM, Andrey Konovalov wrote:
On Fri, Jan 15, 2021 at 1:00 PM Vincenzo Frascino
[off-list ref] wrote:
quoted
[...]
quoted
@@ -60,6 +61,8 @@ static int __init early_kasan_mode(char *arg) if (!strcmp(arg, "off")) kasan_arg_mode = KASAN_ARG_MODE_OFF;+ else if (!strcmp(arg, "light"))+ kasan_arg_mode = KASAN_ARG_MODE_LIGHT;
Hi Vincenzo,
I've just mailed the change to KASAN parameters [1] as discussed, so
we should use a standalone parameter here (kasan.trap?).
Thanks!
[1] https://lkml.org/lkml/2021/1/15/1242
Thanks for this. I will have a look into it today. In the meantime, could you
please elaborate a bit more on kasan.trap?
--
Regards,
Vincenzo
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Thanks for this. I will have a look into it today. In the meantime, could you
please elaborate a bit more on kasan.trap?
That's what I call the boot parameter that allows switching between
sync and async. We'll need one as we're dropping
kasan.mode=off/prod/light/full.
Feel free to name it differently. Perhaps, as kasan.mode is now
unused, we can use that for sync/async.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sat, Jan 16, 2021 at 2:43 PM Vincenzo Frascino
[off-list ref] wrote:
On 1/15/21 3:08 PM, Mark Rutland wrote:
quoted
On Fri, Jan 15, 2021 at 12:00:40PM +0000, Vincenzo Frascino wrote:
quoted
Architectures supported by KASAN HW can provide a light mode of
execution. On an MTE enabled arm64 hw for example this can be identified
with the asynch mode of execution.
In this mode, if a tag check fault occurs, the TFSR_EL1 register is
updated asynchronously. The kernel checks the corresponding bits
periodically.
What's the expected usage of this relative to prod, given that this has
to be chosen at boot time? When/where is this expected to be used
relative to prod mode?
Hi Mark,
Sync + no panic (what is called prod right now) + logging is for the
initial MTE integration stage as causing panics is risky. There's no
way to know how often MTE-detected bugs will happen during normal
usage as the kernel is buggy.
Eventually, we're hoping to switch to sync + panic to allow MTE to act
as a security mitigation. For devices where the slowdown caused by
sync is untolerable, there'll be an option to use async, which is
significantly faster. The exact perf numbers are yet to be measured
properly, I'll share them with one of the future patches.
Thanks!
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Fri, Jan 15, 2021 at 12:00:41PM +0000, Vincenzo Frascino wrote:
quoted
MTE provides an asynchronous mode for detecting tag exceptions. In
particular instead of triggering a fault the arm64 core updates a
register which is checked by the kernel after the asynchronous tag
check fault has occurred.
Add support for MTE asynchronous mode.
The exception handling mechanism will be added with a future patch.
Note: KASAN HW activates async mode via kasan.mode kernel parameter.
The default mode is set to synchronous.
The code that verifies the status of TFSR_EL1 will be added with a
future patch.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
---
arch/arm64/kernel/mte.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
@@ -153,8 +153,30 @@ void mte_init_tags(u64 max_tag)voidmte_enable_kernel(enumkasan_hw_tags_modemode){-/* Enable MTE Sync Mode for EL1. */-sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_SYNC);+constchar*m;++/* Preset parameter values based on the mode. */+switch(mode){+caseKASAN_HW_TAGS_ASYNC:+/* Enable MTE Async Mode for EL1. */+sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_ASYNC);+m="asynchronous";+break;+caseKASAN_HW_TAGS_SYNC:+sysreg_clear_set(sctlr_el1,SCTLR_ELx_TCF_MASK,SCTLR_ELx_TCF_SYNC);+m="synchronous";+break;+default:+/*+*kasanmodeshouldbealwayssethenceweshould+*notreachthiscondition.+*/+WARN_ON_ONCE(1);+return;+}++pr_info_once("MTE: enabled in %s mode at EL1\n",m);+isb();}
For clarity, we should have that ISB before the pr_info_once().
Good point, I will fix it in v4.
As with my comment on patch 1, I think with separate functions this
would be much clearer and simpler:
static inline void __mte_enable_kernel(const char *mode, unsigned long tcf)
{
sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, tcf);
isb();
pr_info_once("MTE: enabled in %s mode at EL1\n", mode);
}
void mte_enable_kernel_sync(void)
{
__mte_enable_kernel("synchronous", SCTLR_ELx_TCF_SYNC);
}
void mte_enable_kernel_async(void)
{
__mte_enable_kernel("asynchronous", SCTLR_ELx_TCF_ASYNC);
}
Ok, seems cleaner like this, will adapt my code accordingly.
On Fri, Jan 15, 2021 at 12:00:43PM +0000, Vincenzo Frascino wrote:
quoted
mte_assign_mem_tag_range() is called on production KASAN HW hot
paths. It makes sense to optimize it in an attempt to reduce the
overhead.
Optimize mte_assign_mem_tag_range() based on the indications provided at
[1].
... what exactly is the optimization?
I /think/ you're just trying to have it inlined, but you should mention
that explicitly.
Good point, I will change it in the next version. I used "Optimize" as a
continuation of the topic in the previous thread but you are right it is not
immediately obvious.
@@ -49,7 +49,31 @@ long get_mte_ctrl(struct task_struct *task);intmte_ptrace_copy_tags(structtask_struct*child,longrequest,unsignedlongaddr,unsignedlongdata);-voidmte_assign_mem_tag_range(void*addr,size_tsize);+staticinlinevoidmte_assign_mem_tag_range(void*addr,size_tsize)+{+u64_addr=(u64)addr;+u64_end=_addr+size;++/*+*ThisfunctionmustbeinvokedfromanMTEenabledcontext.+*+*Note:Theaddressmustbenon-NULLandMTE_GRANULE_SIZEalignedand+*sizemustbenon-zeroandMTE_GRANULE_SIZEaligned.+*/+do{+/*+*'asmvolatile'isrequiredtopreventthecompilertomove+*thestatementoutsideoftheloop.+*/+asmvolatile(__MTE_PREAMBLE"stg %0, [%0]"+:+:"r"(_addr)+:"memory");++_addr+=MTE_GRANULE_SIZE;+}while(_addr<_end);
Is there any chance that this can be used for the last bytes of the
virtual address space? This might need to change to `_addr == _end` if
that is possible, otherwise it'll terminate early in that case.
Theoretically it is a possibility. I will change the condition and add a note
for that.
quoted
+}
What does the code generation look like for this, relative to the
assembly version?
The assembly looks like this:
390: 8b000022 add x2, x1, x0
394: aa0003e1 mov x1, x0
398: d9200821 stg x1, [x1]
39c: 91004021 add x1, x1, #0x10
3a0: eb01005f cmp x2, x1
3a4: 54ffffa8 b.hi 398 <mte_set_mem_tag_range+0x48>
You can see the handcrafted one below.
On Fri, Jan 15, 2021 at 12:00:40PM +0000, Vincenzo Frascino wrote:
quoted
Architectures supported by KASAN HW can provide a light mode of
execution. On an MTE enabled arm64 hw for example this can be identified
with the asynch mode of execution.
In this mode, if a tag check fault occurs, the TFSR_EL1 register is
updated asynchronously. The kernel checks the corresponding bits
periodically.
What's the expected usage of this relative to prod, given that this has
to be chosen at boot time? When/where is this expected to be used
relative to prod mode?
IIUC the light mode is meant for low spec devices. I let Andrey comment a bit
more on this topic.
Rather than passing a mode in, I think it'd be better to have:
* arch_enable_tagging_prod()
* arch_enable_tagging_light()
... that we can map in the arch code to separate:
* mte_enable_kernel_sync()
* mte_enable_kernel_async()
... as by construction that avoids calls with an unhandled mode, and we
wouldn't need the mode enum kasan_hw_tags_mode...
... and we can just have a wrapper like this to call either of the two functions directly, i.e.
static inline void hw_enable_tagging_mode(enum kasan_arg_mode mode)
{
if (mode == KASAN_ARG_MODE_LIGHT)
arch_enable_tagging_mode_light();
else
arch_enable_tagging_mode_prod();
}
Fine by me, this would remove the need of adding a new enumeration as well and
reflect on the arch code. I would keep "arch_enable_tagging_mode_sync" and
"arch_enable_tagging_mode_async" though to give a clear indication in the KASAN
code of the mode we are setting. I will adapt my code accordingly for v4.
Hi Mark,
On 1/16/21 2:22 PM, Vincenzo Frascino wrote:
quoted
Is there any chance that this can be used for the last bytes of the
virtual address space? This might need to change to `_addr == _end` if
that is possible, otherwise it'll terminate early in that case.
Theoretically it is a possibility. I will change the condition and add a note
for that.
I was thinking to the end of the virtual address space scenario and I forgot
that if I use a condition like `_addr == _end` the tagging operation overflows
to the first granule of the next allocation. This disrupts tagging accesses for
that memory area hence I think that `_addr < _end` is the way to go.
--
Regards,
Vincenzo
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Sun, Jan 17, 2021 at 12:27:08PM +0000, Vincenzo Frascino wrote:
quoted
Hi Mark,
On 1/16/21 2:22 PM, Vincenzo Frascino wrote:
quoted
quoted
Is there any chance that this can be used for the last bytes of the
virtual address space? This might need to change to `_addr == _end` if
that is possible, otherwise it'll terminate early in that case.
Theoretically it is a possibility. I will change the condition and add a note
for that.
I was thinking to the end of the virtual address space scenario and I forgot
that if I use a condition like `_addr == _end` the tagging operation overflows
to the first granule of the next allocation. This disrupts tagging accesses for
that memory area hence I think that `_addr < _end` is the way to go.
I think it implies `_addr != _end` is necessary. Otherwise, if `addr` is
PAGE_SIZE from the end of memory, and `size` is PAGE_SIZE, `_end` will
be 0, so using `_addr < _end` will mean the loop will terminate after a
single MTE tag granule rather than the whole page.
Generally, for some addr/increment/size combination (where all are
suitably aligned), you need a pattern like:
| do {
| thing(addr);
| addr += increment;
| } while (addr != end);
... or:
| for (addr = start; addr != end; addr += increment) {
| thing(addr);
| }
... to correctly handle working at the very end of the VA space.
We do similar for page tables, e.g. when we use pmd_addr_end().
Good point! I agree it wraps around otherwise. I will change it accordingly.
Thanks!
Rather than passing a mode in, I think it'd be better to have:
* arch_enable_tagging_prod()
* arch_enable_tagging_light()
... that we can map in the arch code to separate:
* mte_enable_kernel_sync()
* mte_enable_kernel_async()
... as by construction that avoids calls with an unhandled mode, and we
wouldn't need the mode enum kasan_hw_tags_mode...
... and we can just have a wrapper like this to call either of the two functions directly, i.e.
static inline void hw_enable_tagging_mode(enum kasan_arg_mode mode)
{
if (mode == KASAN_ARG_MODE_LIGHT)
arch_enable_tagging_mode_light();
else
arch_enable_tagging_mode_prod();
}
Fine by me, this would remove the need of adding a new enumeration as well and
reflect on the arch code. I would keep "arch_enable_tagging_mode_sync" and
"arch_enable_tagging_mode_async" though to give a clear indication in the KASAN
code of the mode we are setting. I will adapt my code accordingly for v4.
Thanks, that sounds great!
I completely agree on keeping the '_sync' and '_aync' suffixes in the
the core code.
Mark.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Mark commented already, the barrier should be above
mte_check_tfsr_el1_no_sync(). Regarding the ISB, we are waiting for
confirmation from the architects.
While in general we use ISB after a sysreg update, I haven't convinced
myself it's needed here. There's no side-effect to updating this reg and
a subsequent TFSR access should see the new value. If a speculated load
is allowed to update this reg, we'd probably need an ISB+DSB (I don't
think it does, something to check with the architects).
+
+ pr_err("MTE: Asynchronous tag exception detected!");
We discussed this already, I think we should replace this pr_err() with
a call to kasan_report(). In principle, kasan already knows the mode as
it asked for sync/async but we could make this explicit and expand the
kasan API to take some argument (or have separate function like
kasan_report_async()).
--
Catalin
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
+#ifdef CONFIG_KASAN_HW_TAGS
+void mte_check_tfsr_el1_no_sync(void)
+{
+ u64 tfsr_el1;
+
+ if (!system_supports_mte())
+ return;
+
+ tfsr_el1 = read_sysreg_s(SYS_TFSR_EL1);
+
+ /*
+ * The kernel should never hit the condition TF0 == 1
+ * at this point because for the futex code we set
+ * PSTATE.TCO.
+ */
+ WARN_ON(tfsr_el1 & SYS_TFSR_EL1_TF0);
I'd change this to a WARN_ON_ONCE() in case we trip over this due to
model bugs etc. and it floods the log.
I will merge yours and Mark's comment using WARN_ONCE() here. Did not think of
potential bug in the model and you are completely right.
While in general we use ISB after a sysreg update, I haven't convinced
myself it's needed here. There's no side-effect to updating this reg and
a subsequent TFSR access should see the new value.
Why there is no side-effect?
If a speculated load is allowed to update this reg, we'd probably need an
ISB+DSB (I don't think it does, something to check with the architects).
I will check this with the architects and let you know.
--
Regards,
Vincenzo
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
While in general we use ISB after a sysreg update, I haven't convinced
myself it's needed here. There's no side-effect to updating this reg and
a subsequent TFSR access should see the new value.
Why there is no side-effect?
Catalin's saying that the value of TFSR_EL1 doesn't affect anything
other than a read of TFSR_EL1, i.e. there are no indirect reads of
TFSR_EL1 where the value has an effect, so there are no side-effects.
Looking at the ARM ARM, no synchronization is requires from a direct
write to an indirect write (per ARM DDI 0487F.c table D13-1), so I agree
that we don't need the ISB here so long as there are no indirect reads.
Are you aware of cases where the TFSR_EL1 value is read other than by an
MRS? e.g. are there any cases where checks are elided if TF1 is set? If
so, we may need the ISB to order the direct write against subsequent
indirect reads.
Thanks,
Mark.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
While in general we use ISB after a sysreg update, I haven't convinced
myself it's needed here. There's no side-effect to updating this reg and
a subsequent TFSR access should see the new value.
Why there is no side-effect?
Catalin's saying that the value of TFSR_EL1 doesn't affect anything
other than a read of TFSR_EL1, i.e. there are no indirect reads of
TFSR_EL1 where the value has an effect, so there are no side-effects.
Looking at the ARM ARM, no synchronization is requires from a direct
write to an indirect write (per ARM DDI 0487F.c table D13-1), so I agree
that we don't need the ISB here so long as there are no indirect reads.
Are you aware of cases where the TFSR_EL1 value is read other than by an
MRS? e.g. are there any cases where checks are elided if TF1 is set? If
so, we may need the ISB to order the direct write against subsequent
indirect reads.
Thank you for the explanation. I am not aware of any case in which TFSR_EL1 is
read other then by an MRS. Based on the ARM DDI 0487F.c (J1-7626) TF0/TF1 are
always set to '1' without being accessed before. I will check with the
architects for further clarification and if this is correct I will remove the
isb() in the next version.
Are you aware of cases where the TFSR_EL1 value is read other than by an
MRS? e.g. are there any cases where checks are elided if TF1 is set? If
so, we may need the ISB to order the direct write against subsequent
indirect reads.
Thank you for the explanation. I am not aware of any case in which TFSR_EL1 is
read other then by an MRS. Based on the ARM DDI 0487F.c (J1-7626) TF0/TF1 are
always set to '1' without being accessed before. I will check with the
architects for further clarification and if this is correct I will remove the
isb() in the next version.
I spoke to the architects and I confirm that the isb() can be removed.
--
Regards,
Vincenzo
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
If a speculated load is allowed to update this reg, we'd probably need an
ISB+DSB (I don't think it does, something to check with the architects).
I will check this with the architects and let you know.
I spoke to the architects and no speculative load can update TFSR_EL1.
--
Regards,
Vincenzo
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: Mark Rutland <mark.rutland@arm.com> Date: 2021-01-18 20:30:12
On Sun, Jan 17, 2021 at 12:27:08PM +0000, Vincenzo Frascino wrote:
Hi Mark,
On 1/16/21 2:22 PM, Vincenzo Frascino wrote:
quoted
quoted
Is there any chance that this can be used for the last bytes of the
virtual address space? This might need to change to `_addr == _end` if
that is possible, otherwise it'll terminate early in that case.
Theoretically it is a possibility. I will change the condition and add a note
for that.
I was thinking to the end of the virtual address space scenario and I forgot
that if I use a condition like `_addr == _end` the tagging operation overflows
to the first granule of the next allocation. This disrupts tagging accesses for
that memory area hence I think that `_addr < _end` is the way to go.
I think it implies `_addr != _end` is necessary. Otherwise, if `addr` is
PAGE_SIZE from the end of memory, and `size` is PAGE_SIZE, `_end` will
be 0, so using `_addr < _end` will mean the loop will terminate after a
single MTE tag granule rather than the whole page.
Generally, for some addr/increment/size combination (where all are
suitably aligned), you need a pattern like:
| do {
| thing(addr);
| addr += increment;
| } while (addr != end);
... or:
| for (addr = start; addr != end; addr += increment) {
| thing(addr);
| }
... to correctly handle working at the very end of the VA space.
We do similar for page tables, e.g. when we use pmd_addr_end().
Thanks,
Mark.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel