| From: Liu Shixin <liushixin2@huawei.com> |
| Subject: mm/kmemleak: fix partially freeing unknown object warning |
| Date: Wed, 18 Oct 2023 18:29:52 +0800 |
| |
| delete_object_part() can be called by multiple callers in the same time. |
| If an object is found and removed by a caller, and then another caller try |
| to find it too, it failed and return directly. It still be recorded by |
| kmemleak even if it has already been freed to buddy. With DEBUG on, |
| kmemleak will report the following warning, |
| |
| kmemleak: Partially freeing unknown object at 0xa1af86000 (size 4096) |
| CPU: 0 PID: 742 Comm: test_huge Not tainted 6.6.0-rc3kmemleak+ #54 |
| Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014 |
| Call Trace: |
| <TASK> |
| dump_stack_lvl+0x37/0x50 |
| kmemleak_free_part_phys+0x50/0x60 |
| hugetlb_vmemmap_optimize+0x172/0x290 |
| ? __pfx_vmemmap_remap_pte+0x10/0x10 |
| __prep_new_hugetlb_folio+0xe/0x30 |
| prep_new_hugetlb_folio.isra.0+0xe/0x40 |
| alloc_fresh_hugetlb_folio+0xc3/0xd0 |
| alloc_surplus_hugetlb_folio.constprop.0+0x6e/0xd0 |
| hugetlb_acct_memory.part.0+0xe6/0x2a0 |
| hugetlb_reserve_pages+0x110/0x2c0 |
| hugetlbfs_file_mmap+0x11d/0x1b0 |
| mmap_region+0x248/0x9a0 |
| ? hugetlb_get_unmapped_area+0x15c/0x2d0 |
| do_mmap+0x38b/0x580 |
| vm_mmap_pgoff+0xe6/0x190 |
| ksys_mmap_pgoff+0x18a/0x1f0 |
| do_syscall_64+0x3f/0x90 |
| entry_SYSCALL_64_after_hwframe+0x6e/0xd8 |
| |
| Expand __create_object() and move __alloc_object() to the beginning. Then |
| use kmemleak_lock to protect __find_and_remove_object() and |
| __link_object() as a whole, which can guarantee all objects are processed |
| sequentialally. |
| |
| Link: https://lkml.kernel.org/r/20231018102952.3339837-8-liushixin2@huawei.com |
| Fixes: 53238a60dd4a ("kmemleak: Allow partial freeing of memory blocks") |
| Signed-off-by: Liu Shixin <liushixin2@huawei.com> |
| Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> |
| Cc: Kefeng Wang <wangkefeng.wang@huawei.com> |
| Cc: Patrick Wang <patrick.wang.shcn@gmail.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| mm/kmemleak.c | 42 +++++++++++++++++++++++++++++++----------- |
| 1 file changed, 31 insertions(+), 11 deletions(-) |
| |
| --- a/mm/kmemleak.c~mm-kmemleak-fix-partially-freeing-unknown-object-warning |
| +++ a/mm/kmemleak.c |
| @@ -816,16 +816,25 @@ static void delete_object_full(unsigned |
| */ |
| static void delete_object_part(unsigned long ptr, size_t size, bool is_phys) |
| { |
| - struct kmemleak_object *object; |
| - unsigned long start, end; |
| + struct kmemleak_object *object, *object_l, *object_r; |
| + unsigned long start, end, flags; |
| |
| - object = find_and_remove_object(ptr, 1, is_phys); |
| + object_l = __alloc_object(GFP_KERNEL); |
| + if (!object_l) |
| + return; |
| + |
| + object_r = __alloc_object(GFP_KERNEL); |
| + if (!object_r) |
| + goto out; |
| + |
| + raw_spin_lock_irqsave(&kmemleak_lock, flags); |
| + object = __find_and_remove_object(ptr, 1, is_phys); |
| if (!object) { |
| #ifdef DEBUG |
| kmemleak_warn("Partially freeing unknown object at 0x%08lx (size %zu)\n", |
| ptr, size); |
| #endif |
| - return; |
| + goto unlock; |
| } |
| |
| /* |
| @@ -835,14 +844,25 @@ static void delete_object_part(unsigned |
| */ |
| start = object->pointer; |
| end = object->pointer + object->size; |
| - if (ptr > start) |
| - __create_object(start, ptr - start, object->min_count, |
| - GFP_KERNEL, is_phys); |
| - if (ptr + size < end) |
| - __create_object(ptr + size, end - ptr - size, object->min_count, |
| - GFP_KERNEL, is_phys); |
| + if ((ptr > start) && |
| + !__link_object(object_l, start, ptr - start, |
| + object->min_count, is_phys)) |
| + object_l = NULL; |
| + if ((ptr + size < end) && |
| + !__link_object(object_r, ptr + size, end - ptr - size, |
| + object->min_count, is_phys)) |
| + object_r = NULL; |
| + |
| +unlock: |
| + raw_spin_unlock_irqrestore(&kmemleak_lock, flags); |
| + if (object) |
| + __delete_object(object); |
| |
| - __delete_object(object); |
| +out: |
| + if (object_l) |
| + mem_pool_free(object_l); |
| + if (object_r) |
| + mem_pool_free(object_r); |
| } |
| |
| static void __paint_it(struct kmemleak_object *object, int color) |
| _ |