[PATCH v5] arm64: fix the overlap between the kernel image and vmalloc address
zhong jiang
zhongjiang at huawei.com
Fri Jun 2 00:08:58 PDT 2017
Hi, Ard
Thank you for reply.
On 2017/6/2 1:40, Ard Biesheuvel wrote:
> Hi all,
>
> On 1 June 2017 at 13:26, zhongjiang <zhongjiang at huawei.com> wrote:
>> Recently, xiaojun report the following issue.
>>
>> [ 4544.984139] Unable to handle kernel paging request at virtual address ffff804392800000
> This is not a vmalloc address ^^^
The mappings is not at a page granularity. but kernel image maaping use sections.
and this try a bogus walk to the pte level. so it will acess a abnormal address,
not in a vmalloc range.
> [...]
>> I find the issue is introduced when applying commit f9040773b7bb
>> ("arm64: move kernel image to base of vmalloc area"). This patch
>> make the kernel image overlap with vmalloc area. It will result in
>> vmalloc area have the huge page table. but the vmalloc_to_page is
>> not realize the change. and the function is public to any arch.
>>
>> I fix it by adding the another kernel image condition in vmalloc_to_page
>> to make it keep the accordance with previous vmalloc mapping.
>>
> ... so while I agree that there is probably an issue to be solved
> here, I don't see how this patch fixes the problem. This particular
> crash may be caused by an assumption on the part of the kcore code
> that there are no holes in the linear region.
>
>> Fixes: f9040773b7bb ("arm64: move kernel image to base of vmalloc area")
>> Reported-by: tan xiaojun <tanxiaojun at huawei.com>
>> Reviewed-by: Laura Abbott <labbott at redhat.com>
>> Signed-off-by: zhongjiang <zhongjiang at huawei.com>
> So while I think we all agree that the kcore code is likely to get
> confused due to the overlap between vmlinux and the vmalloc region, I
> would like to better understand how it breaks things, and whether we'd
> be better off simply teaching vread/vwrite how to interpret block
> mappings.
I think the root reason is clear. and I test the patch, after applying the patch,
the issue will go away.
> Could you check whether CONFIG_DEBUG_PAGEALLOC makes the issue go away
> (once you have really managed to reproduce it?)
Today, I enable the config and test it in newest kernel version. the issue still exist.
[ 396.495450] [<ffff00000839c400>] __memcpy+0x100/0x180
[ 396.501056] [<ffff00000826ae14>] read_kcore+0x21c/0x3a0
[ 396.506729] [<ffff00000825d37c>] proc_reg_read+0x64/0x90
[ 396.512706] [<ffff0000081f668c>] __vfs_read+0x1c/0xf8
[ 396.518188] [<ffff0000081f792c>] vfs_read+0x84/0x140
[ 396.523653] [<ffff0000081f8df4>] SyS_read+0x44/0xa0
[ 396.529205] [<ffff000008082f30>] el0_svc_naked+0x24/0x28
[ 396.535036] Code: d503201f d503201f d503201f d503201f (a8c12027)
Thanks
zhongjiang
> Thanks,
> Ard.
>
>
>> ---
>> arch/arm64/mm/mmu.c | 2 +-
>> include/linux/vmalloc.h | 1 +
>> mm/vmalloc.c | 31 ++++++++++++++++++++++++-------
>> 3 files changed, 26 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 0c429ec..2265c39 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -509,7 +509,7 @@ static void __init map_kernel_segment(pgd_t *pgd, void *va_start, void *va_end,
>> vma->addr = va_start;
>> vma->phys_addr = pa_start;
>> vma->size = size;
>> - vma->flags = VM_MAP;
>> + vma->flags = VM_KERNEL;
>> vma->caller = __builtin_return_address(0);
>>
>> vm_area_add_early(vma);
>> diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
>> index 0328ce0..c9245af 100644
>> --- a/include/linux/vmalloc.h
>> +++ b/include/linux/vmalloc.h
>> @@ -17,6 +17,7 @@
>> #define VM_ALLOC 0x00000002 /* vmalloc() */
>> #define VM_MAP 0x00000004 /* vmap()ed pages */
>> #define VM_USERMAP 0x00000008 /* suitable for remap_vmalloc_range */
>> +#define VM_KERNEL 0x00000010 /* kernel pages */
>> #define VM_UNINITIALIZED 0x00000020 /* vm_struct is not fully initialized */
>> #define VM_NO_GUARD 0x00000040 /* don't add guard page */
>> #define VM_KASAN 0x00000080 /* has allocated kasan shadow memory */
>> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
>> index 1dda6d8..104fc70 100644
>> --- a/mm/vmalloc.c
>> +++ b/mm/vmalloc.c
>> @@ -1966,12 +1966,25 @@ void *vmalloc_32_user(unsigned long size)
>> }
>> EXPORT_SYMBOL(vmalloc_32_user);
>>
>> +static inline struct page *vmalloc_image_to_page(char *addr,
>> + struct vm_struct *vm)
>> +{
>> + struct page *p = NULL;
>> +
>> + if (vm->flags & VM_KERNEL)
>> + p = virt_to_page(lm_alias(addr));
>> + else
>> + p = vmalloc_to_page(addr);
>> +
>> + return p;
>> +}
>> +
>> /*
>> * small helper routine , copy contents to buf from addr.
>> * If the page is not present, fill zero.
>> */
>> -
>> -static int aligned_vread(char *buf, char *addr, unsigned long count)
>> +static int aligned_vread(char *buf, char *addr, unsigned long count,
>> + struct vm_struct *vm)
>> {
>> struct page *p;
>> int copied = 0;
>> @@ -1983,7 +1996,7 @@ static int aligned_vread(char *buf, char *addr, unsigned long count)
>> length = PAGE_SIZE - offset;
>> if (length > count)
>> length = count;
>> - p = vmalloc_to_page(addr);
>> + p = vmalloc_image_to_page(addr, vm);
>> /*
>> * To do safe access to this _mapped_ area, we need
>> * lock. But adding lock here means that we need to add
>> @@ -2010,7 +2023,8 @@ static int aligned_vread(char *buf, char *addr, unsigned long count)
>> return copied;
>> }
>>
>> -static int aligned_vwrite(char *buf, char *addr, unsigned long count)
>> +static int aligned_vwrite(char *buf, char *addr, unsigned long count,
>> + struct vm_struct *vm)
>> {
>> struct page *p;
>> int copied = 0;
>> @@ -2022,7 +2036,7 @@ static int aligned_vwrite(char *buf, char *addr, unsigned long count)
>> length = PAGE_SIZE - offset;
>> if (length > count)
>> length = count;
>> - p = vmalloc_to_page(addr);
>> + p = vmalloc_image_to_page(addr, vm);
>> /*
>> * To do safe access to this _mapped_ area, we need
>> * lock. But adding lock here means that we need to add
>> @@ -2109,7 +2123,7 @@ long vread(char *buf, char *addr, unsigned long count)
>> if (n > count)
>> n = count;
>> if (!(vm->flags & VM_IOREMAP))
>> - aligned_vread(buf, addr, n);
>> + aligned_vread(buf, addr, n, vm);
>> else /* IOREMAP area is treated as memory hole */
>> memset(buf, 0, n);
>> buf += n;
>> @@ -2190,7 +2204,7 @@ long vwrite(char *buf, char *addr, unsigned long count)
>> if (n > count)
>> n = count;
>> if (!(vm->flags & VM_IOREMAP)) {
>> - aligned_vwrite(buf, addr, n);
>> + aligned_vwrite(buf, addr, n, vm);
>> copied++;
>> }
>> buf += n;
>> @@ -2710,6 +2724,9 @@ static int s_show(struct seq_file *m, void *p)
>> if (v->flags & VM_USERMAP)
>> seq_puts(m, " user");
>>
>> + if (v->flags & VM_KERNEL)
>> + seq_puts(m, " kernel");
>> +
>> if (is_vmalloc_addr(v->pages))
>> seq_puts(m, " vpages");
>>
>> --
>> 1.7.12.4
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> .
>
More information about the linux-arm-kernel
mailing list