Re: [PATCH mm v10 00/42] kasan: add hardware tag-based mode for arm64

From: Vincenzo Frascino
Date: Mon Nov 16 2020 - 09:45:51 EST




On 11/13/20 10:15 PM, Andrey Konovalov wrote:
> This patchset adds a new hardware tag-based mode to KASAN [1]. The new mode
> is similar to the existing software tag-based KASAN, but relies on arm64
> Memory Tagging Extension (MTE) [2] to perform memory and pointer tagging
> (instead of shadow memory and compiler instrumentation).
>
> This patchset is co-developed and tested by
> Vincenzo Frascino <vincenzo.frascino@xxxxxxx>.
>
> This patchset is available here:
>
> https://github.com/xairy/linux/tree/up-kasan-mte-v10
>
> For testing in QEMU hardware tag-based KASAN requires:
>
> 1. QEMU built from master [4] (use "-machine virt,mte=on -cpu max" arguments
> to run).
> 2. GCC version 10.
>
> [1] https://www.kernel.org/doc/html/latest/dev-tools/kasan.html
> [2] https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety
> [3] git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux for-next/mte
> [4] https://github.com/qemu/qemu
>

Tested-by: Vincenzo Frascino <vincenzo.frascino@xxxxxxx>

> ====== Overview
>
> The underlying ideas of the approach used by hardware tag-based KASAN are:
>
> 1. By relying on the Top Byte Ignore (TBI) arm64 CPU feature, pointer tags
> are stored in the top byte of each kernel pointer.
>
> 2. With the Memory Tagging Extension (MTE) arm64 CPU feature, memory tags
> for kernel memory allocations are stored in a dedicated memory not
> accessible via normal instuctions.
>
> 3. On each memory allocation, a random tag is generated, embedded it into
> the returned pointer, and the corresponding memory is tagged with the
> same tag value.
>
> 4. With MTE the CPU performs a check on each memory access to make sure
> that the pointer tag matches the memory tag.
>
> 5. On a tag mismatch the CPU generates a tag fault, and a KASAN report is
> printed.
>
> Same as other KASAN modes, hardware tag-based KASAN is intended as a
> debugging feature at this point.
>
> ====== Rationale
>
> There are two main reasons for this new hardware tag-based mode:
>
> 1. Previously implemented software tag-based KASAN is being successfully
> used on dogfood testing devices due to its low memory overhead (as
> initially planned). The new hardware mode keeps the same low memory
> overhead, and is expected to have significantly lower performance
> impact, due to the tag checks being performed by the hardware.
> Therefore the new mode can be used as a better alternative in dogfood
> testing for hardware that supports MTE.
>
> 2. The new mode lays the groundwork for the planned in-kernel MTE-based
> memory corruption mitigation to be used in production.
>
> ====== Technical details
>
> From the implementation perspective, hardware tag-based KASAN is almost
> identical to the software mode. The key difference is using MTE for
> assigning and checking tags.
>
> Compared to the software mode, the hardware mode uses 4 bits per tag, as
> dictated by MTE. Pointer tags are stored in bits [56:60), the top 4 bits
> have the normal value 0xF. Having less distict tags increases the
> probablity of false negatives (from ~1/256 to ~1/16) in certain cases.
>
> Only synchronous exceptions are set up and used by hardware tag-based KASAN.
>
> ====== Benchmarks
>
> Note: all measurements have been performed with software emulation of Memory
> Tagging Extension, performance numbers for hardware tag-based KASAN on the
> actual hardware are expected to be better.
>
> Boot time [1]:
> * 2.8 sec for clean kernel
> * 5.7 sec for hardware tag-based KASAN
> * 11.8 sec for software tag-based KASAN
> * 11.6 sec for generic KASAN
>
> Slab memory usage after boot [2]:
> * 7.0 kb for clean kernel
> * 9.7 kb for hardware tag-based KASAN
> * 9.7 kb for software tag-based KASAN
> * 41.3 kb for generic KASAN
>
> Measurements have been performed with:
> * defconfig-based configs
> * Manually built QEMU master
> * QEMU arguments: -machine virt,mte=on -cpu max
> * CONFIG_KASAN_STACK_ENABLE disabled
> * CONFIG_KASAN_INLINE enabled
> * clang-10 as the compiler and gcc-10 as the assembler
>
> [1] Time before the ext4 driver is initialized.
> [2] Measured as `cat /proc/meminfo | grep Slab`.
>
> ====== Notes
>
> The cover letter for software tag-based KASAN patchset can be found here:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0116523cfffa62aeb5aa3b85ce7419f3dae0c1b8
>
> ====== History
>
> Change v9->v10:
> (Andrey:)
> - Rebase onto the mm tree.
> - Drop "s390/kasan: include asm/page.h from asm/kasan.h" as linux-next
> already contains this fix queued up with other s390 KASAN changes.
> - Add kfence checks in mm/kasan/hw_tags.c.
> - Rename KASAN_GRANULE_PAGE to KASAN_MEMORY_PER_SHADOW_PAGE.
> - Clarify "slab memory" in documentation.
> - Add a comment to CC_HAS_WORKING_NOSANITIZE_ADDRESS.
> - Drop "kasan: kasan_non_canonical_hook only for software modes" as
> KASAN_INLINE already depends on KASAN_GENERIC || KASAN_SW_TAGS.
> - Mark kasan_init_hw_tags() as __init, as non-init parts have been moved
> to kasan_init_hw_tags_cpu().
> - Rename kasan_(un)poison_memory() to (kasan_)(un)poison_range().
> (Vincenzo:)
> - Add smb_wmb() to copy_highpage().
> - Rename mte_enable() to mte_enable_kernel().
> - Address review comments on selftest code.
> - Address concerns from checkpatch.pl (courtesy of Marco Elver).
> - Integrate a fixup from Marco Elver for code alignment.
>
> Changes v8->v9:
> (Andrey:)
> - Drop __init for kasan_init_hw_tags.
> - Split out mte_enable() from mte_init_tags().
> - Fix incorrect commit author.
> - Revert addr_has_metadata() change done in a previous version, otherwise
> KASAN fails to print metadata for page_alloc allocations.
> - Split kasan_init_hw_tags() into kasan_init_hw_tags_cpu() that is called
> for each CPU in cpu_enable_mte(), and kasan_init_hw_tags() that is called
> for boot CPU in smp_prepare_boot_cpu().
> - Move kunit_kasan_expectation struct definition under CONFIG_KASAN.
> (Vincenzo:)
> - Address Catalin's comments for "arm64: mte: Reset the page tag in
> page->flags".
> - New patch "kasan, mm: untag page address in free_reserved_area".
>
> Changes v7->v8:
> (Andrey:)
> - Rebased onto 5.10-rc2+.
> - Enable in-kernel MTE via kasan_init_hw_tags() instead of doing it
> directly from cpu_enable_mte(). This changes mte_init_tags() to both
> init RRND and enable in-kernel MTE in sync mode.
> - Put the patches back into a logical order.
> - Rename KASAN_SHADOW_GRANULE_SIZE to KASAN_GRANULE_SIZE in
> lib/test_kasan_module.c.
> - Rename kasan_init_tags() to kasan_init_sw_tags() and mark as __init.
>
> Changes v6->v7:
> (Andrey:)
> - Rebase onto 5.10-rc2.
> - Fix pgd_t not defined build warning on s390.
> - Mark kasan_init_depth() as static.
> - Don't use mte_*() functions directly in report_hw_tags.c
> - Add hw_ prefix to KASAN-level definitions of arch_*() tag helpers.
> - Add missing <sched/task_stack.h> include to report_generic.h.
>
> Changes v5->v6:
> (Vincenzo:)
> - Re-based on 5.10-rc1.
> - Modified the flow of the mte fault handler in order to address an issue
> with word at a time routines that would affect Android init process.
> - Dropped Reviewed-by from the involved patches.
> (Andrey:)
> - Properly use #if CONFIG_KASAN_STACK instead of #ifdef
> CONFIG_KASAN_STACK_ENABLE.
> - Expand CONFIG_KASAN checks in arch/arm64/kernel/kaslr.c and
> arch/arm64/kernel/module.c.
> - Don't select CONFIG_CONSTRUCTORS for HW_TAGS mode.
> - Check PageSlab() in addr_has_metadata().
>
> Changes v4->v5:
> (Vincenzo:)
> - Reset the tag associated by the kernel to a page when this is changed by
> the user.
> - Add a kselftest to verify that GCR_EL1 is preserved during context
> switch.
> - Squashed the TBI patch.
> - Addressed some review comments.
> - Verified the series with LTP.
> (Andrey:)
> - Put core arm64 patches first as requested by Catalin.
>
> Changes v3->v4:
> (Vincenzo:)
> - Introduced mte-def.h that contains MTE definitions.
> - Moved __MTE_PREAMBLE in mte.h.
> - Clarified which API is part of mte-kasan.h.
> - Removed tsk argument from mte_set_kernel_gcr().
> - Addressed several nitpicks pointed out during review.
> (Andrey:)
> - Move include <asm/kasan.h> in include/linux/kasan.h to avoid build
> failures.
> - Don't move "select SLUB_DEBUG if SLUB" back and forth between patches
> in KASAN Kconfig.
> - Add arm64 prefix to "kasan: don't allow SW_TAGS with ARM64_MTE" commit.
> - Don't add braces when defining KASAN_GRANULE_SIZE.
> - Make KASAN_HW_TAGS compatible with init_on_alloc/free.
>
> Changes v2->v3:
> (Vincenzo:)
> - Use ARM64_ASM_PREAMBLE for asm macros.
> - Rename mte-helper.h to mte-kasan.h. The new header is meant to contain
> only macros and prototypes directly used in KASAN. The rest is defined
> in mte.h.
> - Update mte_get_mem_tag()/mte_get_random_tag() to used directly asm
> volatile() macros instead of calling library functions.
> - Harden mte_assign_mem_tag_range() to prevent an infinite loop in case of
> unaligned size.
> - Made sure that report_tag_fault() is executed only once.
> - Simplify the mte code in __cpu_setup.
> - Remove kprobes.h from mte.c includes.
> - General cleanup of the code.
> (Andrey:)
> - Use READ/WRITE_ONCE when accessing reported in do_tag_recovery().
> - Move .unreq mte_tcr under CONFIG_ARM64_MTE to avoid build errors when MTE
> is not enabled.
> - Improve mm/kasan/shadow.c comment header.
> - Clarify what is a memory granule in "kasan: rename KASAN_SHADOW_* to
> KASAN_GRANULE_" commit description.
> - Rename (report_)tags_sw/hw.c to to (report_)sw/hw_tags.c and drop
> unnecessary rename commit.
> - Adopt 100 lines limit for some mm/kasan/ changes.
> - Align arguments for stack_trace_save() call in mm/slub.c.
> - Restore comment before kasan_init_tags().
> - Remove GNU headers from all mm/kasan/ files.
> - Simplify check_invalid_free() implementation tag-based modes.
> - Drop subsequently removed report_tag_fault() implementation.
> - Add KASAN_GRANULE_PAGE and use instead of PAGE_SIZE * KASAN_GRANULE_SIZE.
> - Move kasan_enable/disable_current() declarations to simplify
> include/linux/kasan.h.
> - Drop dependency on CONFIG_SLUB_DEBUG.
> - Clarify the purpose of CONFIG_STACKTRACE in KASAN Kconfig.
>
> Changes v1->v2:
> - Rebase onto v10 of the user MTE patchset.
> - Only enable in-kernel MTE when KASAN_HW_TAGS is enabled.
> - Add a layer of arch-level indirection, so KASAN doesn't call MTE helpers
> directly (this will be useful in case more architectures will add support
> for HW_TAGS).
> - Don't do arm64_skip_faulting_instruction() on MTE fault, disable MTE
> instead.
> - Don't allow software tags with MTE via arch/arm64/Kconfig instead of
> lib/Kconfig.kasan.
> - Rename mm/kasan/tags.c to tags_sw.c and mte.c to tags_hw.c, and do the
> same for report_*.c files.
> - Reword HW_TAGS Kconfig help text to make it less MTE specific.
> - Reword and clarify Documentation.
> - Drop unnecessary is_el1_mte_sync_tag_check_fault().
> - Change report_tag_fault() to only call kasan_report() once HW_TAGS is
> introduced.
> - Rename arch/arm64/include/asm/mte_asm.h to mte-helpers.h and move all
> MTE-related defines and some helper functions there.
> - Change mm/kasan/kasan.h to include mte-def.h instead of mte.h.
> - Add WARN_ON() on unaligned size to mte_set_mem_tag_range().
> - Implement ldg/irg MTE routines as inline assembly.
> - Remove smp_wmb() from mte_set_mem_tag_range().
> - Drop __must_check from mte_set_mem_tag_range() as KASAN has no use for
> the return value.
> - Drop zero size check from mte_assign_mem_tag_range().
> - Drop unnecessary include <asm/kasan.h> from low-level arm64 code.
> - Move enabling TBI1 into __cpu_setup().
> - Drop stale comment about callee-saved register from
> arch/arm64/kernel/entry.S.
> - Mark gcr_kernel_excl as __ro_after_init.
> - Use GENMASK() in mte_init_tags().
>
> Andrey Konovalov (33):
> kasan: drop unnecessary GPL text from comment headers
> kasan: KASAN_VMALLOC depends on KASAN_GENERIC
> kasan: group vmalloc code
> kasan: shadow declarations only for software modes
> kasan: rename (un)poison_shadow to (un)poison_range
> kasan: rename KASAN_SHADOW_* to KASAN_GRANULE_*
> kasan: only build init.c for software modes
> kasan: split out shadow.c from common.c
> kasan: define KASAN_MEMORY_PER_SHADOW_PAGE
> kasan: rename report and tags files
> kasan: don't duplicate config dependencies
> kasan: hide invalid free check implementation
> kasan: decode stack frame only with KASAN_STACK_ENABLE
> kasan, arm64: only init shadow for software modes
> kasan, arm64: only use kasan_depth for software modes
> kasan, arm64: move initialization message
> kasan, arm64: rename kasan_init_tags and mark as __init
> kasan: rename addr_has_shadow to addr_has_metadata
> kasan: rename print_shadow_for_address to print_memory_metadata
> kasan: rename SHADOW layout macros to META
> kasan: separate metadata_fetch_row for each mode
> kasan, arm64: don't allow SW_TAGS with ARM64_MTE
> kasan: introduce CONFIG_KASAN_HW_TAGS
> arm64: kasan: Align allocations for HW_TAGS
> arm64: kasan: Add arch layer for memory tagging helpers
> kasan: define KASAN_GRANULE_SIZE for HW_TAGS
> kasan, x86, s390: update undef CONFIG_KASAN
> kasan, arm64: expand CONFIG_KASAN checks
> kasan, arm64: implement HW_TAGS runtime
> kasan, arm64: print report from tag fault handler
> kasan, mm: reset tags when accessing metadata
> kasan, arm64: enable CONFIG_KASAN_HW_TAGS
> kasan: add documentation for hardware tag-based mode
>
> Vincenzo Frascino (9):
> arm64: Enable armv8.5-a asm-arch option
> arm64: mte: Add in-kernel MTE helpers
> arm64: mte: Reset the page tag in page->flags
> arm64: mte: Add in-kernel tag fault handler
> arm64: kasan: Allow enabling in-kernel MTE
> arm64: mte: Convert gcr_user into an exclude mask
> arm64: mte: Switch GCR_EL1 in kernel entry and exit
> kasan, mm: untag page address in free_reserved_area
> kselftest/arm64: Check GCR_EL1 after context switch
>
> Documentation/dev-tools/kasan.rst | 82 ++-
> arch/arm64/Kconfig | 9 +-
> arch/arm64/Makefile | 7 +-
> arch/arm64/include/asm/assembler.h | 2 +-
> arch/arm64/include/asm/cache.h | 3 +
> arch/arm64/include/asm/esr.h | 1 +
> arch/arm64/include/asm/kasan.h | 5 +-
> arch/arm64/include/asm/memory.h | 15 +-
> arch/arm64/include/asm/mte-def.h | 14 +
> arch/arm64/include/asm/mte-kasan.h | 67 ++
> arch/arm64/include/asm/mte.h | 22 +-
> arch/arm64/include/asm/processor.h | 2 +-
> arch/arm64/include/asm/string.h | 5 +-
> arch/arm64/include/asm/uaccess.h | 23 +
> arch/arm64/kernel/asm-offsets.c | 3 +
> arch/arm64/kernel/cpufeature.c | 3 +
> arch/arm64/kernel/entry.S | 41 ++
> arch/arm64/kernel/head.S | 2 +-
> arch/arm64/kernel/hibernate.c | 5 +
> arch/arm64/kernel/image-vars.h | 2 +-
> arch/arm64/kernel/kaslr.c | 3 +-
> arch/arm64/kernel/module.c | 6 +-
> arch/arm64/kernel/mte.c | 118 +++-
> arch/arm64/kernel/setup.c | 2 +-
> arch/arm64/kernel/smp.c | 2 +
> arch/arm64/lib/mte.S | 16 +
> arch/arm64/mm/copypage.c | 9 +
> arch/arm64/mm/fault.c | 59 ++
> arch/arm64/mm/kasan_init.c | 19 +-
> arch/arm64/mm/mteswap.c | 9 +
> arch/arm64/mm/proc.S | 23 +-
> arch/arm64/mm/ptdump.c | 6 +-
> arch/s390/boot/string.c | 1 +
> arch/x86/boot/compressed/misc.h | 1 +
> include/linux/kasan-checks.h | 2 +-
> include/linux/kasan.h | 125 ++--
> include/linux/mm.h | 2 +-
> include/linux/moduleloader.h | 3 +-
> include/linux/page-flags-layout.h | 2 +-
> include/linux/sched.h | 2 +-
> include/linux/string.h | 2 +-
> init/init_task.c | 2 +-
> kernel/fork.c | 4 +-
> lib/Kconfig.kasan | 65 +-
> lib/test_kasan.c | 2 +-
> lib/test_kasan_module.c | 2 +-
> mm/kasan/Makefile | 25 +-
> mm/kasan/common.c | 577 ++----------------
> mm/kasan/generic.c | 51 +-
> mm/kasan/generic_report.c | 165 -----
> mm/kasan/hw_tags.c | 89 +++
> mm/kasan/init.c | 17 +-
> mm/kasan/kasan.h | 74 ++-
> mm/kasan/quarantine.c | 10 -
> mm/kasan/report.c | 256 ++------
> mm/kasan/report_generic.c | 327 ++++++++++
> mm/kasan/report_hw_tags.c | 42 ++
> mm/kasan/{tags_report.c => report_sw_tags.c} | 14 +-
> mm/kasan/shadow.c | 516 ++++++++++++++++
> mm/kasan/{tags.c => sw_tags.c} | 24 +-
> mm/page_alloc.c | 9 +-
> mm/page_poison.c | 2 +-
> mm/ptdump.c | 13 +-
> mm/slab_common.c | 2 +-
> mm/slub.c | 29 +-
> scripts/Makefile.lib | 2 +
> tools/testing/selftests/arm64/mte/Makefile | 2 +-
> .../arm64/mte/check_gcr_el1_cswitch.c | 155 +++++
> 68 files changed, 2036 insertions(+), 1165 deletions(-)
> create mode 100644 arch/arm64/include/asm/mte-def.h
> create mode 100644 arch/arm64/include/asm/mte-kasan.h
> delete mode 100644 mm/kasan/generic_report.c
> create mode 100644 mm/kasan/hw_tags.c
> create mode 100644 mm/kasan/report_generic.c
> create mode 100644 mm/kasan/report_hw_tags.c
> rename mm/kasan/{tags_report.c => report_sw_tags.c} (87%)
> create mode 100644 mm/kasan/shadow.c
> rename mm/kasan/{tags.c => sw_tags.c} (92%)
> create mode 100644 tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
>

--
Regards,
Vincenzo