Merge branch 'for-next/coredump' into for-next/core
authorWill Deacon <will@kernel.org>
Mon, 14 Mar 2022 18:58:46 +0000 (18:58 +0000)
committerWill Deacon <will@kernel.org>
Mon, 14 Mar 2022 18:58:46 +0000 (18:58 +0000)
* for-next/coredump:
  arm64: Change elfcore for_each_mte_vma() to use VMA iterator
  arm64: mte: Document the core dump file format
  arm64: mte: Dump the MTE tags in the core file
  arm64: mte: Define the number of bytes for storing the tags in a page
  elf: Introduce the ARM MTE ELF segment type
  elfcore: Replace CONFIG_{IA64, UML} checks with a new option

12 files changed:
Documentation/arm64/memory-tagging-extension.rst
arch/arm64/Kconfig
arch/arm64/include/asm/mte-def.h
arch/arm64/kernel/Makefile
arch/arm64/kernel/elfcore.c [new file with mode: 0644]
arch/arm64/lib/mte.S
arch/arm64/mm/mteswap.c
arch/ia64/Kconfig
arch/x86/um/Kconfig
fs/Kconfig.binfmt
include/linux/elfcore.h
include/uapi/linux/elf.h

index 7b99c8f..5a70d7a 100644 (file)
@@ -213,6 +213,29 @@ address ABI control and MTE configuration of a process as per the
 Documentation/arm64/tagged-address-abi.rst and above. The corresponding
 ``regset`` is 1 element of 8 bytes (``sizeof(long))``).
 
+Core dump support
+-----------------
+
+The allocation tags for user memory mapped with ``PROT_MTE`` are dumped
+in the core file as additional ``PT_ARM_MEMTAG_MTE`` segments. The
+program header for such segment is defined as:
+
+:``p_type``: ``PT_ARM_MEMTAG_MTE``
+:``p_flags``: 0
+:``p_offset``: segment file offset
+:``p_vaddr``: segment virtual address, same as the corresponding
+  ``PT_LOAD`` segment
+:``p_paddr``: 0
+:``p_filesz``: segment size in file, calculated as ``p_mem_sz / 32``
+  (two 4-bit tags cover 32 bytes of memory)
+:``p_memsz``: segment size in memory, same as the corresponding
+  ``PT_LOAD`` segment
+:``p_align``: 0
+
+The tags are stored in the core file at ``p_offset`` as two 4-bit tags
+in a byte. With the tag granule of 16 bytes, a 4K page requires 128
+bytes in the core file.
+
 Example of correct usage
 ========================
 
index cbcd42d..b55c117 100644 (file)
@@ -10,6 +10,7 @@ config ARM64
        select ACPI_SPCR_TABLE if ACPI
        select ACPI_PPTT if ACPI
        select ARCH_HAS_DEBUG_WX
+       select ARCH_BINFMT_ELF_EXTRA_PHDRS
        select ARCH_BINFMT_ELF_STATE
        select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE
        select ARCH_ENABLE_HUGEPAGE_MIGRATION if HUGETLB_PAGE && MIGRATION
index 626d359..14ee86b 100644 (file)
@@ -11,6 +11,7 @@
 #define MTE_TAG_SHIFT          56
 #define MTE_TAG_SIZE           4
 #define MTE_TAG_MASK           GENMASK((MTE_TAG_SHIFT + (MTE_TAG_SIZE - 1)), MTE_TAG_SHIFT)
+#define MTE_PAGE_TAG_STORAGE   (MTE_GRANULES_PER_PAGE * MTE_TAG_SIZE / 8)
 
 #define __MTE_PREAMBLE         ARM64_ASM_PREAMBLE ".arch_extension memtag\n"
 
index 88b3e2a..986837d 100644 (file)
@@ -61,6 +61,7 @@ obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL)     += acpi_parking_protocol.o
 obj-$(CONFIG_PARAVIRT)                 += paravirt.o
 obj-$(CONFIG_RANDOMIZE_BASE)           += kaslr.o
 obj-$(CONFIG_HIBERNATION)              += hibernate.o hibernate-asm.o
+obj-$(CONFIG_ELF_CORE)                 += elfcore.o
 obj-$(CONFIG_KEXEC_CORE)               += machine_kexec.o relocate_kernel.o    \
                                           cpu-reset.o
 obj-$(CONFIG_KEXEC_FILE)               += machine_kexec_file.o kexec_image.o
diff --git a/arch/arm64/kernel/elfcore.c b/arch/arm64/kernel/elfcore.c
new file mode 100644 (file)
index 0000000..3ed39c6
--- /dev/null
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/coredump.h>
+#include <linux/elfcore.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/cpufeature.h>
+#include <asm/mte.h>
+
+#ifndef VMA_ITERATOR
+#define VMA_ITERATOR(name, mm, addr)   \
+       struct mm_struct *name = mm
+#define for_each_vma(vmi, vma)         \
+       for (vma = vmi->mmap; vma; vma = vma->vm_next)
+#endif
+
+#define for_each_mte_vma(vmi, vma)                                     \
+       if (system_supports_mte())                                      \
+               for_each_vma(vmi, vma)                                  \
+                       if (vma->vm_flags & VM_MTE)
+
+static unsigned long mte_vma_tag_dump_size(struct vm_area_struct *vma)
+{
+       if (vma->vm_flags & VM_DONTDUMP)
+               return 0;
+
+       return vma_pages(vma) * MTE_PAGE_TAG_STORAGE;
+}
+
+/* Derived from dump_user_range(); start/end must be page-aligned */
+static int mte_dump_tag_range(struct coredump_params *cprm,
+                             unsigned long start, unsigned long end)
+{
+       unsigned long addr;
+
+       for (addr = start; addr < end; addr += PAGE_SIZE) {
+               char tags[MTE_PAGE_TAG_STORAGE];
+               struct page *page = get_dump_page(addr);
+
+               /*
+                * get_dump_page() returns NULL when encountering an empty
+                * page table entry that would otherwise have been filled with
+                * the zero page. Skip the equivalent tag dump which would
+                * have been all zeros.
+                */
+               if (!page) {
+                       dump_skip(cprm, MTE_PAGE_TAG_STORAGE);
+                       continue;
+               }
+
+               /*
+                * Pages mapped in user space as !pte_access_permitted() (e.g.
+                * PROT_EXEC only) may not have the PG_mte_tagged flag set.
+                */
+               if (!test_bit(PG_mte_tagged, &page->flags)) {
+                       put_page(page);
+                       dump_skip(cprm, MTE_PAGE_TAG_STORAGE);
+                       continue;
+               }
+
+               mte_save_page_tags(page_address(page), tags);
+               put_page(page);
+               if (!dump_emit(cprm, tags, MTE_PAGE_TAG_STORAGE))
+                       return 0;
+       }
+
+       return 1;
+}
+
+Elf_Half elf_core_extra_phdrs(void)
+{
+       struct vm_area_struct *vma;
+       int vma_count = 0;
+       VMA_ITERATOR(vmi, current->mm, 0);
+
+       for_each_mte_vma(vmi, vma)
+               vma_count++;
+
+       return vma_count;
+}
+
+int elf_core_write_extra_phdrs(struct coredump_params *cprm, loff_t offset)
+{
+       struct vm_area_struct *vma;
+       VMA_ITERATOR(vmi, current->mm, 0);
+
+       for_each_mte_vma(vmi, vma) {
+               struct elf_phdr phdr;
+
+               phdr.p_type = PT_ARM_MEMTAG_MTE;
+               phdr.p_offset = offset;
+               phdr.p_vaddr = vma->vm_start;
+               phdr.p_paddr = 0;
+               phdr.p_filesz = mte_vma_tag_dump_size(vma);
+               phdr.p_memsz = vma->vm_end - vma->vm_start;
+               offset += phdr.p_filesz;
+               phdr.p_flags = 0;
+               phdr.p_align = 0;
+
+               if (!dump_emit(cprm, &phdr, sizeof(phdr)))
+                       return 0;
+       }
+
+       return 1;
+}
+
+size_t elf_core_extra_data_size(void)
+{
+       struct vm_area_struct *vma;
+       size_t data_size = 0;
+       VMA_ITERATOR(vmi, current->mm, 0);
+
+       for_each_mte_vma(vmi, vma)
+               data_size += mte_vma_tag_dump_size(vma);
+
+       return data_size;
+}
+
+int elf_core_write_extra_data(struct coredump_params *cprm)
+{
+       struct vm_area_struct *vma;
+       VMA_ITERATOR(vmi, current->mm, 0);
+
+       for_each_mte_vma(vmi, vma) {
+               if (vma->vm_flags & VM_DONTDUMP)
+                       continue;
+
+               if (!mte_dump_tag_range(cprm, vma->vm_start, vma->vm_end))
+                       return 0;
+       }
+
+       return 1;
+}
index f531dcb..8590af3 100644 (file)
@@ -134,7 +134,7 @@ SYM_FUNC_END(mte_copy_tags_to_user)
 /*
  * Save the tags in a page
  *   x0 - page address
- *   x1 - tag storage
+ *   x1 - tag storage, MTE_PAGE_TAG_STORAGE bytes
  */
 SYM_FUNC_START(mte_save_page_tags)
        multitag_transfer_size x7, x5
@@ -158,7 +158,7 @@ SYM_FUNC_END(mte_save_page_tags)
 /*
  * Restore the tags in a page
  *   x0 - page address
- *   x1 - tag storage
+ *   x1 - tag storage, MTE_PAGE_TAG_STORAGE bytes
  */
 SYM_FUNC_START(mte_restore_page_tags)
        multitag_transfer_size x7, x5
index 7c4ef56..a9e50e9 100644 (file)
@@ -12,7 +12,7 @@ static DEFINE_XARRAY(mte_pages);
 void *mte_allocate_tag_storage(void)
 {
        /* tags granule is 16 bytes, 2 tags stored per byte */
-       return kmalloc(PAGE_SIZE / 16 / 2, GFP_KERNEL);
+       return kmalloc(MTE_PAGE_TAG_STORAGE, GFP_KERNEL);
 }
 
 void mte_free_tag_storage(char *storage)
index a7e0157..e003b24 100644 (file)
@@ -8,6 +8,7 @@ menu "Processor type and features"
 
 config IA64
        bool
+       select ARCH_BINFMT_ELF_EXTRA_PHDRS
        select ARCH_HAS_DMA_MARK_CLEAN
        select ARCH_HAS_STRNCPY_FROM_USER
        select ARCH_HAS_STRNLEN_USER
index 40d6a06..ead7e5b 100644 (file)
@@ -8,6 +8,7 @@ endmenu
 
 config UML_X86
        def_bool y
+       select ARCH_BINFMT_ELF_EXTRA_PHDRS if X86_32
 
 config 64BIT
        bool "64-bit kernel" if "$(SUBARCH)" = "x86"
index 4d5ae61..68e5862 100644 (file)
@@ -36,6 +36,9 @@ config COMPAT_BINFMT_ELF
 config ARCH_BINFMT_ELF_STATE
        bool
 
+config ARCH_BINFMT_ELF_EXTRA_PHDRS
+       bool
+
 config ARCH_HAVE_ELF_PROT
        bool
 
index 746e081..f8e206e 100644 (file)
@@ -114,7 +114,7 @@ static inline int elf_core_copy_task_fpregs(struct task_struct *t, struct pt_reg
 #endif
 }
 
-#if (defined(CONFIG_UML) && defined(CONFIG_X86_32)) || defined(CONFIG_IA64)
+#ifdef CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS
 /*
  * These functions parameterize elf_core_dump in fs/binfmt_elf.c to write out
  * extra segments containing the gate DSO contents.  Dumping its
@@ -149,6 +149,6 @@ static inline size_t elf_core_extra_data_size(void)
 {
        return 0;
 }
-#endif
+#endif /* CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS */
 
 #endif /* _LINUX_ELFCORE_H */
index 61bf477..fe8e5b7 100644 (file)
@@ -40,6 +40,9 @@ typedef __s64 Elf64_Sxword;
 
 #define PT_GNU_STACK   (PT_LOOS + 0x474e551)
 
+/* ARM MTE memory tag segment type */
+#define PT_ARM_MEMTAG_MTE      (PT_LOPROC + 0x1)
+
 /*
  * Extended Numbering
  *