aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorAndrey Ryabinin <aryabinin@virtuozzo.com>2018-05-25 17:47:38 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-05-25 21:12:11 -0400
commit0f901dcbc31f88ae41a2aaa365f7802b5d520a28 (patch)
tree49cf2f300275f997ca2c72fb8715748f8e205b89 /mm
parentb9ddff9b85036292f8b6d4ac37e21fc229fedea1 (diff)
mm/kasan: don't vfree() nonexistent vm_area
KASAN uses different routines to map shadow for hot added memory and memory obtained in boot process. Attempt to offline memory onlined by normal boot process leads to this: Trying to vfree() nonexistent vm area (000000005d3b34b9) WARNING: CPU: 2 PID: 13215 at mm/vmalloc.c:1525 __vunmap+0x147/0x190 Call Trace: kasan_mem_notifier+0xad/0xb9 notifier_call_chain+0x166/0x260 __blocking_notifier_call_chain+0xdb/0x140 __offline_pages+0x96a/0xb10 memory_subsys_offline+0x76/0xc0 device_offline+0xb8/0x120 store_mem_state+0xfa/0x120 kernfs_fop_write+0x1d5/0x320 __vfs_write+0xd4/0x530 vfs_write+0x105/0x340 SyS_write+0xb0/0x140 Obviously we can't call vfree() to free memory that wasn't allocated via vmalloc(). Use find_vm_area() to see if we can call vfree(). Unfortunately it's a bit tricky to properly unmap and free shadow allocated during boot, so we'll have to keep it. If memory will come online again that shadow will be reused. Matthew asked: how can you call vfree() on something that isn't a vmalloc address? vfree() is able to free any address returned by __vmalloc_node_range(). And __vmalloc_node_range() gives you any address you ask. It doesn't have to be an address in [VMALLOC_START, VMALLOC_END] range. That's also how the module_alloc()/module_memfree() works on architectures that have designated area for modules. [aryabinin@virtuozzo.com: improve comments] Link: http://lkml.kernel.org/r/dabee6ab-3a7a-51cd-3b86-5468718e0390@virtuozzo.com [akpm@linux-foundation.org: fix typos, reflow comment] Link: http://lkml.kernel.org/r/20180201163349.8700-1-aryabinin@virtuozzo.com Fixes: fa69b5989bb0 ("mm/kasan: add support for memory hotplug") Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com> Reported-by: Paul Menzel <pmenzel+linux-kasan-dev@molgen.mpg.de> Cc: Alexander Potapenko <glider@google.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/kasan/kasan.c63
1 files changed, 61 insertions, 2 deletions
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index bc0e68f7dc75..7160028efd12 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -792,6 +792,40 @@ DEFINE_ASAN_SET_SHADOW(f5);
792DEFINE_ASAN_SET_SHADOW(f8); 792DEFINE_ASAN_SET_SHADOW(f8);
793 793
794#ifdef CONFIG_MEMORY_HOTPLUG 794#ifdef CONFIG_MEMORY_HOTPLUG
795static bool shadow_mapped(unsigned long addr)
796{
797 pgd_t *pgd = pgd_offset_k(addr);
798 p4d_t *p4d;
799 pud_t *pud;
800 pmd_t *pmd;
801 pte_t *pte;
802
803 if (pgd_none(*pgd))
804 return false;
805 p4d = p4d_offset(pgd, addr);
806 if (p4d_none(*p4d))
807 return false;
808 pud = pud_offset(p4d, addr);
809 if (pud_none(*pud))
810 return false;
811
812 /*
813 * We can't use pud_large() or pud_huge(), the first one is
814 * arch-specific, the last one depends on HUGETLB_PAGE. So let's abuse
815 * pud_bad(), if pud is bad then it's bad because it's huge.
816 */
817 if (pud_bad(*pud))
818 return true;
819 pmd = pmd_offset(pud, addr);
820 if (pmd_none(*pmd))
821 return false;
822
823 if (pmd_bad(*pmd))
824 return true;
825 pte = pte_offset_kernel(pmd, addr);
826 return !pte_none(*pte);
827}
828
795static int __meminit kasan_mem_notifier(struct notifier_block *nb, 829static int __meminit kasan_mem_notifier(struct notifier_block *nb,
796 unsigned long action, void *data) 830 unsigned long action, void *data)
797{ 831{
@@ -813,6 +847,14 @@ static int __meminit kasan_mem_notifier(struct notifier_block *nb,
813 case MEM_GOING_ONLINE: { 847 case MEM_GOING_ONLINE: {
814 void *ret; 848 void *ret;
815 849
850 /*
851 * If shadow is mapped already than it must have been mapped
852 * during the boot. This could happen if we onlining previously
853 * offlined memory.
854 */
855 if (shadow_mapped(shadow_start))
856 return NOTIFY_OK;
857
816 ret = __vmalloc_node_range(shadow_size, PAGE_SIZE, shadow_start, 858 ret = __vmalloc_node_range(shadow_size, PAGE_SIZE, shadow_start,
817 shadow_end, GFP_KERNEL, 859 shadow_end, GFP_KERNEL,
818 PAGE_KERNEL, VM_NO_GUARD, 860 PAGE_KERNEL, VM_NO_GUARD,
@@ -824,8 +866,25 @@ static int __meminit kasan_mem_notifier(struct notifier_block *nb,
824 kmemleak_ignore(ret); 866 kmemleak_ignore(ret);
825 return NOTIFY_OK; 867 return NOTIFY_OK;
826 } 868 }
827 case MEM_OFFLINE: 869 case MEM_OFFLINE: {
828 vfree((void *)shadow_start); 870 struct vm_struct *vm;
871
872 /*
873 * shadow_start was either mapped during boot by kasan_init()
874 * or during memory online by __vmalloc_node_range().
875 * In the latter case we can use vfree() to free shadow.
876 * Non-NULL result of the find_vm_area() will tell us if
877 * that was the second case.
878 *
879 * Currently it's not possible to free shadow mapped
880 * during boot by kasan_init(). It's because the code
881 * to do that hasn't been written yet. So we'll just
882 * leak the memory.
883 */
884 vm = find_vm_area((void *)shadow_start);
885 if (vm)
886 vfree((void *)shadow_start);
887 }
829 } 888 }
830 889
831 return NOTIFY_OK; 890 return NOTIFY_OK;