Skip to content

Commit

Permalink
Merge branch 'for-next/coredump' into for-next/core
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
willdeacon committed Mar 14, 2022
2 parents dfd42fa + 3a4f7ef commit 0d3d031
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 5 deletions.
23 changes: 23 additions & 0 deletions Documentation/arm64/memory-tagging-extension.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
========================

Expand Down
1 change: 1 addition & 0 deletions arch/arm64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/include/asm/mte-def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
1 change: 1 addition & 0 deletions arch/arm64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
134 changes: 134 additions & 0 deletions arch/arm64/kernel/elfcore.c
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 2 additions & 2 deletions arch/arm64/lib/mte.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/mm/mteswap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions arch/ia64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arch/x86/um/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 3 additions & 0 deletions fs/Kconfig.binfmt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions include/linux/elfcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
3 changes: 3 additions & 0 deletions include/uapi/linux/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down

0 comments on commit 0d3d031

Please sign in to comment.