mm: kmemleak: store objects allocated with physical address separately and check when scan
Subsystems (2)
v2 cover mm: kmemleak: store objects allocated with physical address separately and check when scan
The kmemleak_*_phys() interface uses "min_low_pfn" and "max_low_pfn" to check address. But on some architectures, kmemleak_*_phys() is called before those two variables initialized. The following steps will be taken:
1) Add OBJECT_PHYS flag and rbtree for the objects allocated with physical address
2) Store physical address in objects if allocated with OBJECT_PHYS
3) Check the boundary when scan instead of in kmemleak_*_phys()
This patch set will solve:
https://lore.kernel.org/r/[email protected]
https://lore.kernel.org/r/[email protected]
v1: https://lore.kernel.org/r/[email protected]
v1->v2:
- add rbtree for the objects allocated with physical address
- store physical address in objects if allocated with OBJECT_PHYS
- check the upper object boundary as well and avoid duplicate check
Patrick Wang (4):
mm: kmemleak: add OBJECT_PHYS flag for objects allocated with physical
address
mm: kmemleak: add rbtree for objects allocated with physical address
mm: kmemleak: handle address stored in object based on its type
mm: kmemleak: kmemleak_*_phys() set address type and check PA when
scan
mm/kmemleak.c | 193 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 70 deletions(-)
Signature · 1 line
2.25.1
PATCH v2 1/4 mm: kmemleak: add OBJECT_PHYS flag for objects allocated with physical address
Add OBJECT_PHYS flag for object. This flag is used to identify the objects allocated with physical address.The create_object() function is added an argument to set that flag.
--- mm/kmemleak.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-)
mm/kmemleak.c +13 -10
@@ -172,6 +172,8 @@ struct kmemleak_object {#define OBJECT_NO_SCAN (1 << 2)/* flag set to fully scan the object when scan_area allocation failed */ #define OBJECT_FULL_SCAN (1 << 3) +/* flag set for object allocated with physical address */ +#define OBJECT_PHYS (1 << 4)#define HEX_PREFIX " "/* number of bytes to print per line; must be 16 or 32 */
@@ -575,7 +577,8 @@ static int __save_stack_trace(unsigned long *trace)* memory block and add it to the object_list and object_tree_root. */ static struct kmemleak_object *create_object (unsigned long ptr,size_t size, -int min_count,gfp_t gfp) +int min_count,gfp_t gfp, +bool is_phys) {unsigned long flags;struct kmemleak_object *object, *parent;
@@ -595,7 +598,7 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,INIT_HLIST_HEAD (&object->area_list);raw_spin_lock_init (&object->lock);atomic_set (&object->use_count,1 ); - object->flags =OBJECT_ALLOCATED ; + object->flags =OBJECT_ALLOCATED | (is_phys ?OBJECT_PHYS :0 ); object->pointer = ptr; object->size =kfence_ksize ((void *)ptr) ?: size; object->excess_ref =0 ;
@@ -729,10 +732,10 @@ static void delete_object_part(unsigned long ptr, size_t size) end = object->pointer + object->size;if (ptr > start)create_object (start, ptr - start, object->min_count, -GFP_KERNEL ); +GFP_KERNEL , object->flags &OBJECT_PHYS );if (ptr + size < end)create_object (ptr + size, end - ptr - size, object->min_count, -GFP_KERNEL ); +GFP_KERNEL , object->flags &OBJECT_PHYS );__delete_object (object); }
@@ -904,7 +907,7 @@ void __ref kmemleak_alloc(const void *ptr, size_t size, int min_count,pr_debug ("%s(0x%p, %zu, %d)\n" , __func__, ptr, size, min_count);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -create_object ((unsigned long )ptr, size, min_count, gfp); +create_object ((unsigned long )ptr, size, min_count, gfp, false); }EXPORT_SYMBOL_GPL (kmemleak_alloc);
@@ -931,7 +934,7 @@ void __ref kmemleak_alloc_percpu(const void __percpu *ptr, size_t size,if (kmemleak_enabled && ptr && !IS_ERR (ptr))for_each_possible_cpu (cpu)create_object ((unsigned long )per_cpu_ptr (ptr, cpu), - size,0 , gfp); + size,0 , gfp, false); }EXPORT_SYMBOL_GPL (kmemleak_alloc_percpu);
@@ -953,7 +956,7 @@ void __ref kmemleak_vmalloc(const struct vm_struct *area, size_t size, gfp_t gfp* the virtual address of the vmalloc'ed block. */ if (kmemleak_enabled) { -create_object ((unsigned long )area->addr, size,2 , gfp); +create_object ((unsigned long )area->addr, size,2 , gfp, false);object_set_excess_ref ((unsigned long )area, (unsigned long )area->addr); }
@@ -1966,14 +1969,14 @@ void __init kmemleak_init(void)/* register the data/bss sections */ create_object ((unsigned long )_sdata, _edata - _sdata, -KMEMLEAK_GREY ,GFP_ATOMIC ); +KMEMLEAK_GREY ,GFP_ATOMIC , false);create_object ((unsigned long )__bss_start, __bss_stop - __bss_start, -KMEMLEAK_GREY ,GFP_ATOMIC ); +KMEMLEAK_GREY ,GFP_ATOMIC , false);/* only register .data..ro_after_init if not within .data */ if (&__start_ro_after_init < &_sdata || &__end_ro_after_init > &_edata)create_object ((unsigned long )__start_ro_after_init, __end_ro_after_init - __start_ro_after_init, -KMEMLEAK_GREY ,GFP_ATOMIC ); +KMEMLEAK_GREY ,GFP_ATOMIC , false); }/*
Signature · 1 line
2.25.1
Catalin Marinas
maintainerRe: [PATCH v2 1/4] mm: kmemleak: add OBJECT_PHYS flag for objects allocated with physical address
Quoted 14 lines · Patrick Wang
mm/kmemleak.c +2
-0@@ -172,6 +172,8 @@ struct kmemleak_object {#define OBJECT_NO_SCAN (1 << 2)/* flag set to fully scan the object when scan_area allocation failed */ #define OBJECT_FULL_SCAN (1 << 3) +/* flag set for object allocated with physical address */ +#define OBJECT_PHYS (1 << 4)#define HEX_PREFIX " "/* number of bytes to print per line; must be 16 or 32 */
mm/kmemleak.c static int __save_stack_trace(unsigned long *trace) · line 581
Quoted 6 lines
mm/kmemleak.c +2
-1@@ -575,7 +577,8 @@ static int __save_stack_trace(unsigned long *trace)* memory block and add it to the object_list and object_tree_root. */ static struct kmemleak_object *create_object (unsigned long ptr,size_t size, -int min_count,gfp_t gfp) +int min_count,gfp_t gfp, +bool is_phys)
The patch looks fine but I wonder whether we should have different functions for is_phys true/false, we may end up fewer changes overall since most places simply pass is_phys == false:
static struct kmemleak_object *__create_object(unsigned long ptr, size_t size,
int min_count, gfp_t gfp, bool is_phys);
static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
int min_count, gfp_t gfp)
{
return __create_object(ptr, size, min_count, gfp, false);
}static struct kmemleak_object *create_object_phys(unsigned long ptr, size_t size,
int min_count, gfp_t gfp)
{
return __create_object(ptr, size, min_count, gfp, true);
}Same for the other patches that change a few more functions.
Signature · 1 line
Catalin
Re: [PATCH v2 1/4] mm: kmemleak: add OBJECT_PHYS flag for objects allocated with physical address
Quoted 12 lines · Catalin Marinas
Quoted 11 lines · Patrick Wang
mm/kmemleak.c +2
-0@@ -172,6 +172,8 @@ struct kmemleak_object {#define OBJECT_NO_SCAN (1 << 2)/* flag set to fully scan the object when scan_area allocation failed */ #define OBJECT_FULL_SCAN (1 << 3) +/* flag set for object allocated with physical address */ +#define OBJECT_PHYS (1 << 4)
Quoted 13 lines
mm/kmemleak.c static int __save_stack_trace(unsigned long *trace) · line 581
Quoted 9 lines
mm/kmemleak.c +2
-1#define HEX_PREFIX " "/* number of bytes to print per line; must be 16 or 32 */ @@ -575,7 +577,8 @@ static int __save_stack_trace(unsigned long *trace)* memory block and add it to the object_list and object_tree_root. */ static struct kmemleak_object *create_object (unsigned long ptr,size_t size, -int min_count,gfp_t gfp) +int min_count,gfp_t gfp, +bool is_phys)The patch looks fine but I wonder whether we should have different functions for
is_phystrue/false, we may end up fewer changes overall since most places simply passis_phys== false:
This should be better. Will do.
Quoted 11 lines
static struct
kmemleak_object*__create_object(unsigned long ptr,size_tsize,int min_count, gfp_t gfp, bool is_phys);static struct
kmemleak_object*create_object(unsigned long ptr,size_tsize,
intmin_count,gfp_tgfp){ return __create_object(ptr, size, min_count, gfp, false); }
Quoted 7 lines
static struct
kmemleak_object*create_object_phys(unsigned long ptr,size_tsize,
intmin_count,gfp_tgfp){ return __create_object(ptr, size, min_count, gfp, true); }Same for the other patches that change a few more functions.
Since the physical objects are only used as gray objects. Changes on irrelevant places will be removed.
The leak check could be taken on physical objects. Conversion of block address from virtual to physical before lookup should make this work (this is useless currently). I think we'd better know about this.
Thanks,
Patrick
Catalin Marinas
maintainerRe: [PATCH v2 1/4] mm: kmemleak: add OBJECT_PHYS flag for objects allocated with physical address
Quoted 4 lines · Patrick Wang
The leak check could be taken on physical objects. Conversion of block address from virtual to physical before lookup should make this work (this is useless currently). I think we'd better know about this.
Yes, we could add this, but since all the phys objects are currently 'gray', it won't make any difference, other than an extra lookup in the phys tree.
Signature · 1 line
Catalin
PATCH v2 2/4 mm: kmemleak: add rbtree for objects allocated with physical address
In reply to Patrick WangAdd object_phys_tree_root to store the objects allocated with physical address. Distinguish it from object_tree_root by OBJECT_PHYS flag or function argument.
--- mm/kmemleak.c | 99 +++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 42 deletions(-)
mm/kmemleak.c +57 -42
@@ -14,14 +14,16 @@* The following locks and mutexes are used by kmemleak: * * - kmemleak_lock (raw_spinlock_t): protects the object_list modifications and -* accesses to the object_tree_root. The object_list is the main list -* holding the metadata (struct kmemleak_object) for the allocated memory -* blocks. The object_tree_root is a red black tree used to look-up -* metadata based on a pointer to the corresponding memory block. The -* kmemleak_object structures are added to the object_list and -* object_tree_root in the create_object() function called from the -* kmemleak_alloc() callback and removed in delete_object() called from the -* kmemleak_free() callback +* accesses to the object_tree_root (or object_phys_tree_root). The +* object_list is the main list holding the metadata (struct kmemleak_object) +* for the allocated memory blocks. The object_tree_root and object_phys_tree_root +* are red black trees used to look-up metadata based on a pointer to the +* corresponding memory block. The object_phys_tree_root is for objects +* allocated with physical address. The kmemleak_object structures are +* added to the object_list and object_tree_root (or object_phys_tree_root) +* in the create_object() function called from the kmemleak_alloc() (or +* kmemleak_alloc_phys()) callback and removed in delete_object() called from +* the kmemleak_free() callback * - kmemleak_object.lock (raw_spinlock_t): protects a kmemleak_object. * Accesses to the metadata (e.g. count) are protected by this lock. Note * that some members of this structure may be protected by other means
@@ -195,7 +197,9 @@ static int mem_pool_free_count = ARRAY_SIZE(mem_pool);static LIST_HEAD (mem_pool_free_list);/* search tree for object boundaries */ static struct rb_root object_tree_root =RB_ROOT ; -/* protecting the access to object_list and object_tree_root */ +/* search tree for object (with OBJECT_PHYS flag) boundaries */ +static struct rb_root object_phys_tree_root =RB_ROOT ; +/* protecting the access to object_list, object_tree_root (or object_phys_tree_root) */ static DEFINE_RAW_SPINLOCK (kmemleak_lock);/* allocation caches for kmemleak internal data */
@@ -380,9 +384,11 @@ static void dump_object_info(struct kmemleak_object *object)* beginning of the memory block are allowed. The kmemleak_lock must be held * when calling this function. */ -static struct kmemleak_object *lookup_object (unsigned long ptr,int alias) +static struct kmemleak_object *lookup_object (unsigned long ptr,int alias, +bool is_phys) { -struct rb_node *rb = object_tree_root.rb_node; +struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node : + object_tree_root.rb_node;unsigned long untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr);while (rb) {
@@ -517,14 +523,15 @@ static void put_object(struct kmemleak_object *object)/* * Look up an object in the object search tree and increase its use_count. */ -static struct kmemleak_object *find_and_get_object (unsigned long ptr,int alias) +static struct kmemleak_object *find_and_get_object (unsigned long ptr,int alias, +bool is_phys) {unsigned long flags;struct kmemleak_object *object;rcu_read_lock ();raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =lookup_object (ptr, alias, is_phys);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);/* check whether the object is still available */
@@ -536,27 +543,32 @@ static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias) }/* -* Remove an object from the object_tree_root and object_list. Must be called -* with the kmemleak_lock held _if_ kmemleak is still enabled. +* Remove an object from the object_tree_root (or object_phys_tree_root) +* and object_list. Must be called with the kmemleak_lock held _if_ kmemleak +* is still enabled. */ static void __remove_object (struct kmemleak_object *object) { -rb_erase (&object->rb_node, &object_tree_root); +rb_erase (&object->rb_node, object->flags &OBJECT_PHYS ? + &object_phys_tree_root : + &object_tree_root);list_del_rcu (&object->object_list); }/* * Look up an object in the object search tree and remove it from both -* object_tree_root and object_list. The returned object's use_count should be -* at least 1, as initially set by create_object(). +* object_tree_root (or object_phys_tree_root) and object_list. The +* returned object's use_count should be at least 1, as initially set +* by create_object(). */ -static struct kmemleak_object *find_and_remove_object (unsigned long ptr,int alias) +static struct kmemleak_object *find_and_remove_object (unsigned long ptr,int alias, +bool is_phys) {unsigned long flags;struct kmemleak_object *object;raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =lookup_object (ptr, alias, is_phys);if (object)__remove_object (object);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);
@@ -574,7 +586,8 @@ static int __save_stack_trace(unsigned long *trace)/* * Create the metadata (struct kmemleak_object) corresponding to an allocated -* memory block and add it to the object_list and object_tree_root. +* memory block and add it to the object_list and object_tree_root (or +* object_phys_tree_root). */ static struct kmemleak_object *create_object(unsigned long ptr, size_t size, int min_count, gfp_t gfp,
@@ -633,7 +646,8 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size, untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); min_addr =min (min_addr, untagged_ptr); max_addr =max (max_addr, untagged_ptr + size); - link = &object_tree_root.rb_node; + link = is_phys ? &object_phys_tree_root.rb_node : + &object_tree_root.rb_node; rb_parent =NULL ;while (*link) { rb_parent = *link;
@@ -657,7 +671,8 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size, } }rb_link_node (&object->rb_node, rb_parent, link); -rb_insert_color (&object->rb_node, &object_tree_root); +rb_insert_color (&object->rb_node, is_phys ? &object_phys_tree_root : + &object_tree_root);list_add_tail_rcu (&object->object_list, &object_list); out:
@@ -693,7 +708,7 @@ static void delete_object_full(unsigned long ptr) {struct kmemleak_object *object; - object =find_and_remove_object (ptr,0 ); + object =find_and_remove_object (ptr,0 , false);if (!object) {#ifdef DEBUG kmemleak_warn("Freeing unknown object at 0x%08lx\n",
@@ -709,12 +724,12 @@ static void delete_object_full(unsigned long ptr)* delete it. If the memory block is partially freed, the function may create * additional metadata for the remaining parts of the block. */ -static void delete_object_part (unsigned long ptr,size_t size) +static void delete_object_part (unsigned long ptr,size_t size,bool is_phys) {struct kmemleak_object *object;unsigned long start, end; - object =find_and_remove_object (ptr,1 ); + 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",
@@ -756,11 +771,11 @@ static void paint_it(struct kmemleak_object *object, int color)raw_spin_unlock_irqrestore (&object->lock, flags); } -static void paint_ptr (unsigned long ptr,int color) +static void paint_ptr (unsigned long ptr,int color,bool is_phys) {struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =find_and_get_object (ptr,0 , is_phys);if (!object) { kmemleak_warn("Trying to color unknown object at 0x%08lx as %s\n", ptr,
@@ -776,18 +791,18 @@ static void paint_ptr(unsigned long ptr, int color)* Mark an object permanently as gray-colored so that it can no longer be * reported as a leak. This is used in general to mark a false positive. */ -static void make_gray_object (unsigned long ptr) +static void make_gray_object (unsigned long ptr,bool is_phys) { -paint_ptr (ptr,KMEMLEAK_GREY ); +paint_ptr (ptr,KMEMLEAK_GREY , is_phys); }/* * Mark the object as black-colored so that it is ignored from scans and * reporting. */ -static void make_black_object (unsigned long ptr) +static void make_black_object (unsigned long ptr,bool is_phys) { -paint_ptr (ptr,KMEMLEAK_BLACK ); +paint_ptr (ptr,KMEMLEAK_BLACK , is_phys); }/*
@@ -802,7 +817,7 @@ static void add_scan_area(unsigned long ptr, size_t size, gfp_t gfp)unsigned long untagged_ptr;unsigned long untagged_objp; - object =find_and_get_object (ptr,1 ); + object =find_and_get_object (ptr,1 , false);if (!object) {kmemleak_warn ("Adding scan area to unknown object at 0x%08lx\n" , ptr);
@@ -852,7 +867,7 @@ static void object_set_excess_ref(unsigned long ptr, unsigned long excess_ref)unsigned long flags;struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =find_and_get_object (ptr,0 , false);if (!object) {kmemleak_warn ("Setting excess_ref on unknown object at 0x%08lx\n" , ptr);
@@ -875,7 +890,7 @@ static void object_no_scan(unsigned long ptr)unsigned long flags;struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =find_and_get_object (ptr,0 , false);if (!object) {kmemleak_warn ("Not scanning unknown object at 0x%08lx\n" , ptr);return ;
@@ -993,7 +1008,7 @@ void __ref kmemleak_free_part(const void *ptr, size_t size)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -delete_object_part ((unsigned long )ptr, size); +delete_object_part ((unsigned long )ptr, size, false); }EXPORT_SYMBOL_GPL (kmemleak_free_part);
@@ -1034,7 +1049,7 @@ void __ref kmemleak_update_trace(const void *ptr)if (!kmemleak_enabled ||IS_ERR_OR_NULL (ptr))return ; - object =find_and_get_object ((unsigned long )ptr,1 ); + object =find_and_get_object ((unsigned long )ptr,1 , false);if (!object) {#ifdef DEBUG kmemleak_warn("Updating stack trace for unknown object at %p\n",
@@ -1063,7 +1078,7 @@ void __ref kmemleak_not_leak(const void *ptr)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -make_gray_object ((unsigned long )ptr); +make_gray_object ((unsigned long )ptr, false); }EXPORT_SYMBOL (kmemleak_not_leak);
@@ -1081,7 +1096,7 @@ void __ref kmemleak_ignore(const void *ptr)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -make_black_object ((unsigned long )ptr); +make_black_object ((unsigned long )ptr, false); }EXPORT_SYMBOL (kmemleak_ignore);
@@ -1275,7 +1290,7 @@ static void scan_block(void *_start, void *_end,* is still present in object_tree_root and object_list * (with updates protected by kmemleak_lock). */ - object =lookup_object (pointer,1 ); + object =lookup_object (pointer,1 , false);if (!object)continue ;if (object == scanned)
@@ -1299,7 +1314,7 @@ static void scan_block(void *_start, void *_end,raw_spin_unlock (&object->lock);if (excess_ref) { - object =lookup_object (excess_ref,0 ); + object =lookup_object (excess_ref,0 , false);if (!object)continue ;if (object == scanned)
@@ -1728,7 +1743,7 @@ static int dump_str_object_info(const char *str)if (kstrtoul (str,0 , &addr))return -EINVAL ; - object =find_and_get_object (addr,0 ); + object =find_and_get_object (addr,0 , false);if (!object) {pr_info ("Unknown object at 0x%08lx\n" , addr);return -EINVAL ;
Signature · 1 line
2.25.1
Catalin Marinas
maintainerRe: [PATCH v2 2/4] mm: kmemleak: add rbtree for objects allocated with physical address
Quoted 6 lines · Patrick Wang
mm/kmemleak.c +0
-2@@ -536,27 +543,32 @@ static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias) }/* -* Remove an object from the object_tree_root and object_list. Must be called -* with the kmemleak_lock held _if_ kmemleak is still enabled.
mm/kmemleak.c static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias) · line 554
Quoted 10 lines
mm/kmemleak.c +6
-1+* Remove an object from the object_tree_root (or object_phys_tree_root) +* and object_list. Must be called with the kmemleak_lock held _if_ kmemleak +* is still enabled. */ static void __remove_object (struct kmemleak_object *object) { -rb_erase (&object->rb_node, &object_tree_root); +rb_erase (&object->rb_node, object->flags &OBJECT_PHYS ? + &object_phys_tree_root : + &object_tree_root);
This pattern appears in a few place, I guess it's better with a macro, say get_object_tree_root(object). But see how many are left, I have some comments below on reducing the diff.
Quoted 5 lines
mm/kmemleak.c +0
-1@@ -709,12 +724,12 @@ static void delete_object_full(unsigned long ptr)* delete it. If the memory block is partially freed, the function may create * additional metadata for the remaining parts of the block. */ -static void delete_object_part (unsigned long ptr,size_t size)
mm/kmemleak.c static void delete_object_full(unsigned long ptr) · line 735
Quoted 10 lines
mm/kmemleak.c +2
-1+static void delete_object_part (unsigned long ptr,size_t size,bool is_phys) {struct kmemleak_object *object;unsigned long start, end; - object =find_and_remove_object (ptr,1 ); + 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",
The previous patch introduced a check on object->flags for delete_object_part(). I think you can just use is_phys directly now when calling create_object().
Quoted 25 lines
mm/kmemleak.c +4
-4@@ -756,11 +771,11 @@ static void paint_it(struct kmemleak_object *object, int color)raw_spin_unlock_irqrestore (&object->lock, flags); } -static void paint_ptr (unsigned long ptr,int color) +static void paint_ptr (unsigned long ptr,int color,bool is_phys) {struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =find_and_get_object (ptr,0 , is_phys);if (!object) { kmemleak_warn("Trying to color unknown object at 0x%08lx as %s\n", ptr,@@ -776,18 +791,18 @@ static void paint_ptr(unsigned long ptr, int color)* Mark an object permanently as gray-colored so that it can no longer be * reported as a leak. This is used in general to mark a false positive. */ -static void make_gray_object (unsigned long ptr) +static void make_gray_object (unsigned long ptr,bool is_phys) { -paint_ptr (ptr,KMEMLEAK_GREY ); +paint_ptr (ptr,KMEMLEAK_GREY , is_phys); }
mm/kmemleak.c static void paint_ptr(unsigned long ptr, int color) · line 806
Quoted 10 lines
mm/kmemleak.c +2
-2/* * Mark the object as black-colored so that it is ignored from scans and * reporting. */ -static void make_black_object (unsigned long ptr) +static void make_black_object (unsigned long ptr,bool is_phys) { -paint_ptr (ptr,KMEMLEAK_BLACK ); +paint_ptr (ptr,KMEMLEAK_BLACK , is_phys); }
We won't need any of these functions to get an 'is_phys' argument if we make kmemleak_alloc_phys() always create gray objects (do this as one of the first patches in the series).
Quoted 21 lines
mm/kmemleak.c +2
-2/* @@ -802,7 +817,7 @@ static void add_scan_area(unsigned long ptr, size_t size, gfp_t gfp)unsigned long untagged_ptr;unsigned long untagged_objp; - object =find_and_get_object (ptr,1 ); + object =find_and_get_object (ptr,1 , false);if (!object) {kmemleak_warn ("Adding scan area to unknown object at 0x%08lx\n" , ptr);@@ -852,7 +867,7 @@ static void object_set_excess_ref(unsigned long ptr, unsigned long excess_ref)unsigned long flags;struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =find_and_get_object (ptr,0 , false);if (!object) {kmemleak_warn ("Setting excess_ref on unknown object at 0x%08lx\n" , ptr);
mm/kmemleak.c static void object_no_scan(unsigned long ptr) · line 896
Quoted 8 lines
mm/kmemleak.c +1
-1@@ -875,7 +890,7 @@ static void object_no_scan(unsigned long ptr)unsigned long flags;struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =find_and_get_object (ptr,0 , false);if (!object) {kmemleak_warn ("Not scanning unknown object at 0x%08lx\n" , ptr);return ;
Same for these.
Quoted 28 lines
mm/kmemleak.c +3
-3@@ -993,7 +1008,7 @@ void __ref kmemleak_free_part(const void *ptr, size_t size)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -delete_object_part ((unsigned long )ptr, size); +delete_object_part ((unsigned long )ptr, size, false); }EXPORT_SYMBOL_GPL (kmemleak_free_part);@@ -1034,7 +1049,7 @@ void __ref kmemleak_update_trace(const void *ptr)if (!kmemleak_enabled ||IS_ERR_OR_NULL (ptr))return ; - object =find_and_get_object ((unsigned long )ptr,1 ); + object =find_and_get_object ((unsigned long )ptr,1 , false);if (!object) {#ifdef DEBUG kmemleak_warn("Updating stack trace for unknown object at %p\n",@@ -1063,7 +1078,7 @@ void __ref kmemleak_not_leak(const void *ptr)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -make_gray_object ((unsigned long )ptr); +make_gray_object ((unsigned long )ptr, false); }EXPORT_SYMBOL (kmemleak_not_leak);
mm/kmemleak.c void __ref kmemleak_ignore(const void *ptr) · line 1101
Quoted 7 lines
mm/kmemleak.c +1
-1@@ -1081,7 +1096,7 @@ void __ref kmemleak_ignore(const void *ptr)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -make_black_object ((unsigned long )ptr); +make_black_object ((unsigned long )ptr, false); }EXPORT_SYMBOL (kmemleak_ignore);
If we avoid changing make_*_object(), we won't need these anymore.
Quoted 19 lines
mm/kmemleak.c +2
-2@@ -1275,7 +1290,7 @@ static void scan_block(void *_start, void *_end,* is still present in object_tree_root and object_list * (with updates protected by kmemleak_lock). */ - object =lookup_object (pointer,1 ); + object =lookup_object (pointer,1 , false);if (!object)continue ;if (object == scanned)@@ -1299,7 +1314,7 @@ static void scan_block(void *_start, void *_end,raw_spin_unlock (&object->lock);if (excess_ref) { - object =lookup_object (excess_ref,0 ); + object =lookup_object (excess_ref,0 , false);if (!object)continue ;if (object == scanned)
mm/kmemleak.c static int dump_str_object_info(const char *str) · line 1749
Quoted 8 lines
mm/kmemleak.c +1
-1@@ -1728,7 +1743,7 @@ static int dump_str_object_info(const char *str)if (kstrtoul (str,0 , &addr))return -EINVAL ; - object =find_and_get_object (addr,0 ); + object =find_and_get_object (addr,0 , false);if (!object) {pr_info ("Unknown object at 0x%08lx\n" , addr);return -EINVAL ;
I think find_and_get_object() is never called on a phys object, so you can probably simplify these a bit. Just add an is_phys argument where strictly necessary and maybe even add a separate function like lookup_object_phys() to reduce the other changes.
Signature · 1 line
Catalin
Re: [PATCH v2 2/4] mm: kmemleak: add rbtree for objects allocated with physical address
Quoted 13 lines · Catalin Marinas
Quoted 12 lines · Patrick Wang
mm/kmemleak.c +3
-2@@ -536,27 +543,32 @@ static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias) }/* -* Remove an object from the object_tree_root and object_list. Must be called -* with the kmemleak_lock held _if_ kmemleak is still enabled. +* Remove an object from the object_tree_root (or object_phys_tree_root) +* and object_list. Must be called with the kmemleak_lock held _if_ kmemleak +* is still enabled. */ static void __remove_object (struct kmemleak_object *object) {
Quoted 8 lines
mm/kmemleak.c static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias) · line 554
Quoted 4 lines
mm/kmemleak.c +3
-1-rb_erase (&object->rb_node, &object_tree_root); +rb_erase (&object->rb_node, object->flags &OBJECT_PHYS ? + &object_phys_tree_root : + &object_tree_root);This pattern appears in a few place, I guess it's better with a macro, say
get_object_tree_root(object). But see how many are left, I have some comments below on reducing the diff.
Will do.
Quoted 11 lines
Quoted 11 lines
mm/kmemleak.c +1
-1@@ -709,12 +724,12 @@ static void delete_object_full(unsigned long ptr)* delete it. If the memory block is partially freed, the function may create * additional metadata for the remaining parts of the block. */ -static void delete_object_part (unsigned long ptr,size_t size) +static void delete_object_part (unsigned long ptr,size_t size,bool is_phys) {struct kmemleak_object *object;unsigned long start, end;
Quoted 9 lines
mm/kmemleak.c static void delete_object_full(unsigned long ptr) · line 735
Quoted 5 lines
mm/kmemleak.c +1
-1- object =find_and_remove_object (ptr,1 ); + 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",The previous patch introduced a check on
object->flagsfordelete_object_part(). I think you can just useis_physdirectly now when callingcreate_object().
Will do.
Quoted 21 lines
Quoted 21 lines
mm/kmemleak.c +2
-2@@ -1275,7 +1290,7 @@ static void scan_block(void *_start, void *_end,* is still present in object_tree_root and object_list * (with updates protected by kmemleak_lock). */ - object =lookup_object (pointer,1 ); + object =lookup_object (pointer,1 , false);if (!object)continue ;if (object == scanned)@@ -1299,7 +1314,7 @@ static void scan_block(void *_start, void *_end,raw_spin_unlock (&object->lock);if (excess_ref) { - object =lookup_object (excess_ref,0 ); + object =lookup_object (excess_ref,0 , false);if (!object)continue ;if (object == scanned)@@ -1728,7 +1743,7 @@ static int dump_str_object_info(const char *str)
Quoted 12 lines
mm/kmemleak.c static int dump_str_object_info(const char *str) · line 1749
Quoted 7 lines
mm/kmemleak.c +1
-1if (kstrtoul (str,0 , &addr))return -EINVAL ; - object =find_and_get_object (addr,0 ); + object =find_and_get_object (addr,0 , false);if (!object) {pr_info ("Unknown object at 0x%08lx\n" , addr);return -EINVAL ;I think
find_and_get_object()is never called on a phys object, so you can probably simplify these a bit. Just add anis_physargument where strictly necessary and maybe even add a separate function likelookup_object_phys()to reduce the other changes.
Will add lookup_object_phys() function and find_and_get_object_phys() function. The find_and_get_object() function is also called in many places.
Thanks,
Patrick
PATCH v2 3/4 mm: kmemleak: handle address stored in object based on its type
In reply to Patrick WangTreat the address stored in object in different way according to its type:
- Only use kasan_reset_tag for virtual address
- Only update min_addr and max_addr for virtual address
- Convert physical address to virtual address in scan_object
--- mm/kmemleak.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-)
mm/kmemleak.c +24 -10
@@ -297,7 +297,9 @@ static void hex_dump_object(struct seq_file *seq,warn_or_seq_printf (seq," hex dump (first %zu bytes):\n" , len);kasan_disable_current ();warn_or_seq_hex_dump (seq,DUMP_PREFIX_NONE ,HEX_ROW_SIZE , -HEX_GROUP_SIZE ,kasan_reset_tag ((void *)ptr), len,HEX_ASCII ); +HEX_GROUP_SIZE , object->flags &OBJECT_PHYS ? ptr : +kasan_reset_tag ((void *)ptr), + len,HEX_ASCII );kasan_enable_current (); }
@@ -389,14 +391,15 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias, {struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node : object_tree_root.rb_node; -unsigned long untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); +unsigned long untagged_ptr = is_phys ? ptr : (unsigned long )kasan_reset_tag ((void *)ptr);while (rb) {struct kmemleak_object *object;unsigned long untagged_objp; object = rb_entry(rb, struct kmemleak_object, rb_node); - untagged_objp = (unsigned long )kasan_reset_tag ((void *)object->pointer); + untagged_objp = is_phys ? object->pointer : + (unsigned long )kasan_reset_tag ((void *)object->pointer);if (untagged_ptr < untagged_objp) rb = object->rb_node.rb_left;
@@ -643,16 +646,19 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,raw_spin_lock_irqsave (&kmemleak_lock, flags); - untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); - min_addr =min (min_addr, untagged_ptr); - max_addr =max (max_addr, untagged_ptr + size); + untagged_ptr = is_phys ? ptr : (unsigned long )kasan_reset_tag ((void *)ptr); +if (!is_phys) { + min_addr =min (min_addr, untagged_ptr); + max_addr =max (max_addr, untagged_ptr + size); + } link = is_phys ? &object_phys_tree_root.rb_node : &object_tree_root.rb_node; rb_parent =NULL ;while (*link) { rb_parent = *link; parent = rb_entry(rb_parent, struct kmemleak_object, rb_node); - untagged_objp = (unsigned long )kasan_reset_tag ((void *)parent->pointer); + untagged_objp = is_phys ? parent->pointer : + (unsigned long )kasan_reset_tag ((void *)parent->pointer);if (untagged_ptr + size <= untagged_objp) link = &parent->rb_node.rb_left;else if (untagged_objp + parent->size <= untagged_ptr)
@@ -1202,7 +1208,9 @@ static bool update_checksum(struct kmemleak_object *object)kasan_disable_current ();kcsan_disable_current (); - object->checksum =crc32 (0 ,kasan_reset_tag ((void *)object->pointer), object->size); + object->checksum =crc32 (0 , object->flags &OBJECT_PHYS ? (void *)object->pointer : +kasan_reset_tag ((void *)object->pointer), + object->size);kasan_enable_current ();kcsan_enable_current ();
@@ -1353,6 +1361,7 @@ static void scan_object(struct kmemleak_object *object) {struct kmemleak_scan_area *area;unsigned long flags; +void *obj_ptr;/* * Once the object->lock is acquired, the corresponding memory block
@@ -1364,10 +1373,15 @@ static void scan_object(struct kmemleak_object *object)if (!(object->flags &OBJECT_ALLOCATED ))/* already freed object */ goto out; + + obj_ptr = object->flags &OBJECT_PHYS ? +__va ((void *)object->pointer) : + (void *)object->pointer; +if (hlist_empty (&object->area_list) || object->flags &OBJECT_FULL_SCAN ) { -void *start = (void *)object->pointer; -void *end = (void *)(object->pointer + object->size); +void *start = obj_ptr; +void *end = obj_ptr + object->size;void *next; do {
Signature · 1 line
2.25.1
Catalin Marinas
maintainerRe: [PATCH v2 3/4] mm: kmemleak: handle address stored in object based on its type
Quoted 18 lines · Patrick Wang
Treat the address stored in object in different way according to its type:
- Only use
kasan_reset_tagfor virtual address
- Only updatemin_addrandmax_addrfor virtual address
- Convert physical address to virtual address inscan_objectSuggested-by Catalin Marinas <[email protected]>Signed-off-by Patrick Wang <[email protected]>--- mm/kmemleak.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-)mm/kmemleak.c +0
-0
mm/kmemleak.c static void hex_dump_object(struct seq_file *seq, · line 304
Quoted 9 lines
mm/kmemleak.c +3
-1@@ -297,7 +297,9 @@ static void hex_dump_object(struct seq_file *seq,warn_or_seq_printf (seq," hex dump (first %zu bytes):\n" , len);kasan_disable_current ();warn_or_seq_hex_dump (seq,DUMP_PREFIX_NONE ,HEX_ROW_SIZE , -HEX_GROUP_SIZE ,kasan_reset_tag ((void *)ptr), len,HEX_ASCII ); +HEX_GROUP_SIZE , object->flags &OBJECT_PHYS ? ptr : +kasan_reset_tag ((void *)ptr), + len,HEX_ASCII );kasan_enable_current (); }
This will go wrong since ptr is the actual physical address, it cannot be dereferenced. This should only be used on virtual pointers and this is the case already as we never print unreferenced objects from the phys tree. What we could do though is something like an early exit from this function (together with a comment that it doesn't support dumping such objects):
if (WARN_ON_ONCE(object->flags & OBJECT_PHYS)) return;
Quoted 9 lines
mm/kmemleak.c +1
-1@@ -389,14 +391,15 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias, {struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node : object_tree_root.rb_node; -unsigned long untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); +unsigned long untagged_ptr = is_phys ? ptr : (unsigned long )kasan_reset_tag ((void *)ptr);while (rb) {
mm/kmemleak.c static struct kmemleak_object *lookup_object(unsigned long ptr, int alias, · line 405
Quoted 10 lines
mm/kmemleak.c +2
-1struct kmemleak_object *object;unsigned long untagged_objp; object = rb_entry(rb, struct kmemleak_object, rb_node); - untagged_objp = (unsigned long )kasan_reset_tag ((void *)object->pointer); + untagged_objp = is_phys ? object->pointer : + (unsigned long )kasan_reset_tag ((void *)object->pointer);if (untagged_ptr < untagged_objp) rb = object->rb_node.rb_left;
You could leave this unchanged. A phys pointer is already untagged, so it wouldn't make any difference.
mm/kmemleak.c static struct kmemleak_object *create_object(unsigned long ptr, size_t size, · line 649
Quoted 8 lines
mm/kmemleak.c +1
-3@@ -643,16 +646,19 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,raw_spin_lock_irqsave (&kmemleak_lock, flags); - untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); - min_addr =min (min_addr, untagged_ptr); - max_addr =max (max_addr, untagged_ptr + size); + untagged_ptr = is_phys ? ptr : (unsigned long )kasan_reset_tag ((void *)ptr);
Same here.
mm/kmemleak.c static struct kmemleak_object *create_object(unsigned long ptr, size_t size, · line 661
Quoted 13 lines
mm/kmemleak.c +6
-1+if (!is_phys) { + min_addr =min (min_addr, untagged_ptr); + max_addr =max (max_addr, untagged_ptr + size); + } link = is_phys ? &object_phys_tree_root.rb_node : &object_tree_root.rb_node; rb_parent =NULL ;while (*link) { rb_parent = *link; parent = rb_entry(rb_parent, struct kmemleak_object, rb_node); - untagged_objp = (unsigned long )kasan_reset_tag ((void *)parent->pointer); + untagged_objp = is_phys ? parent->pointer : + (unsigned long )kasan_reset_tag ((void *)parent->pointer);
And here.
mm/kmemleak.c static bool update_checksum(struct kmemleak_object *object) · line 1213
Quoted 11 lines
mm/kmemleak.c +3
-1if (untagged_ptr + size <= untagged_objp) link = &parent->rb_node.rb_left;else if (untagged_objp + parent->size <= untagged_ptr)@@ -1202,7 +1208,9 @@ static bool update_checksum(struct kmemleak_object *object)kasan_disable_current ();kcsan_disable_current (); - object->checksum =crc32 (0 ,kasan_reset_tag ((void *)object->pointer), object->size); + object->checksum =crc32 (0 , object->flags &OBJECT_PHYS ? (void *)object->pointer : +kasan_reset_tag ((void *)object->pointer), + object->size);
Luckily that's never called on a phys object, otherwise *object->pointer would segfault. As for hex_dump, just return early with a warning if that's the case.
Quoted 19 lines
mm/kmemleak.c +5
-0kasan_enable_current ();kcsan_enable_current ();@@ -1353,6 +1361,7 @@ static void scan_object(struct kmemleak_object *object) {struct kmemleak_scan_area *area;unsigned long flags; +void *obj_ptr;/* * Once the object->lock is acquired, the corresponding memory block @@ -1364,10 +1373,15 @@ static void scan_object(struct kmemleak_object *object)if (!(object->flags &OBJECT_ALLOCATED ))/* already freed object */ goto out; + + obj_ptr = object->flags &OBJECT_PHYS ? +__va ((void *)object->pointer) : + (void *)object->pointer;
mm/kmemleak.c static void scan_object(struct kmemleak_object *object) · line 1387
Quoted 10 lines
mm/kmemleak.c +3
-2+if (hlist_empty (&object->area_list) || object->flags &OBJECT_FULL_SCAN ) { -void *start = (void *)object->pointer; -void *end = (void *)(object->pointer + object->size); +void *start = obj_ptr; +void *end = obj_ptr + object->size;void *next; do {
This looks fine, assuming that the following patch adds the checks for objects above max_low_pfn (I haven't got there yet).
Signature · 1 line
Catalin
Re: [PATCH v2 3/4] mm: kmemleak: handle address stored in object based on its type
Quoted 36 lines · Catalin Marinas
Quoted 18 lines · Patrick Wang
Treat the address stored in object in different way according to its type:
- Only use
kasan_reset_tagfor virtual address
- Only updatemin_addrandmax_addrfor virtual address
- Convert physical address to virtual address inscan_objectSuggested-by Catalin Marinas <[email protected]>Signed-off-by Patrick Wang <[email protected]>--- mm/kmemleak.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-)mm/kmemleak.c +0
-0mm/kmemleak.c static void hex_dump_object(struct seq_file *seq, · line 304
Quoted 9 lines
mm/kmemleak.c +3
-1@@ -297,7 +297,9 @@ static void hex_dump_object(struct seq_file *seq,warn_or_seq_printf (seq," hex dump (first %zu bytes):\n" , len);kasan_disable_current ();warn_or_seq_hex_dump (seq,DUMP_PREFIX_NONE ,HEX_ROW_SIZE , -HEX_GROUP_SIZE ,kasan_reset_tag ((void *)ptr), len,HEX_ASCII ); +HEX_GROUP_SIZE , object->flags &OBJECT_PHYS ? ptr : +kasan_reset_tag ((void *)ptr), + len,HEX_ASCII );kasan_enable_current (); }This will go wrong since ptr is the actual physical address, it cannot be dereferenced. This should only be used on virtual pointers and this is the case already as we never print unreferenced objects from the phys tree. What we could do though is something like an early exit from this function (together with a comment that it doesn't support dumping such objects):
Quoted 3 lines
if (WARN_ON_ONCE(object->flags & OBJECT_PHYS)) return;
I also found this. Will do.
Quoted 17 lines
Quoted 17 lines
mm/kmemleak.c +3
-2@@ -389,14 +391,15 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias, {struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node : object_tree_root.rb_node; -unsigned long untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); +unsigned long untagged_ptr = is_phys ? ptr : (unsigned long )kasan_reset_tag ((void *)ptr);while (rb) {struct kmemleak_object *object;unsigned long untagged_objp; object = rb_entry(rb, struct kmemleak_object, rb_node); - untagged_objp = (unsigned long )kasan_reset_tag ((void *)object->pointer); + untagged_objp = is_phys ? object->pointer : + (unsigned long )kasan_reset_tag ((void *)object->pointer);
Quoted 5 lines
mm/kmemleak.c static struct kmemleak_object *lookup_object(unsigned long ptr, int alias, · line 405
Quoted 2 lines
mm/kmemleak.c +0
-0if (untagged_ptr < untagged_objp) rb = object->rb_node.rb_left;You could leave this unchanged. A phys pointer is already untagged, so it wouldn't make any difference.
Right, will do.
Quoted 11 lines
mm/kmemleak.c static struct kmemleak_object *create_object(unsigned long ptr, size_t size, · line 649
Quoted 9 lines
mm/kmemleak.c +1
-3@@ -643,16 +646,19 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,raw_spin_lock_irqsave (&kmemleak_lock, flags); - untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); - min_addr =min (min_addr, untagged_ptr); - max_addr =max (max_addr, untagged_ptr + size); + untagged_ptr = is_phys ? ptr : (unsigned long )kasan_reset_tag ((void *)ptr);Same here.
Will do.
Quoted 16 lines
Quoted 4 lines
mm/kmemleak.c +3
-0+if (!is_phys) { + min_addr =min (min_addr, untagged_ptr); + max_addr =max (max_addr, untagged_ptr + size);mm/kmemleak.c static struct kmemleak_object *create_object(unsigned long ptr, size_t size, · line 661
Quoted 10 lines
mm/kmemleak.c +3
-1+ } link = is_phys ? &object_phys_tree_root.rb_node : &object_tree_root.rb_node; rb_parent =NULL ;while (*link) { rb_parent = *link; parent = rb_entry(rb_parent, struct kmemleak_object, rb_node); - untagged_objp = (unsigned long )kasan_reset_tag ((void *)parent->pointer); + untagged_objp = is_phys ? parent->pointer : + (unsigned long )kasan_reset_tag ((void *)parent->pointer);And here.
Will do.
Quoted 6 lines
Quoted 6 lines
mm/kmemleak.c +0
-0if (untagged_ptr + size <= untagged_objp) link = &parent->rb_node.rb_left;else if (untagged_objp + parent->size <= untagged_ptr)@@ -1202,7 +1208,9 @@ static bool update_checksum(struct kmemleak_object *object)
Quoted 10 lines
mm/kmemleak.c static bool update_checksum(struct kmemleak_object *object) · line 1213
Quoted 6 lines
mm/kmemleak.c +3
-1kasan_disable_current ();kcsan_disable_current (); - object->checksum =crc32 (0 ,kasan_reset_tag ((void *)object->pointer), object->size); + object->checksum =crc32 (0 , object->flags &OBJECT_PHYS ? (void *)object->pointer : +kasan_reset_tag ((void *)object->pointer), + object->size);Luckily that's never called on a phys object, otherwise
*object->pointerwould segfault. As forhex_dump, just return early with a warning if that's the case.
Right, will do.
Thanks,
Patrick
PATCH v2 4/4 mm: kmemleak: kmemleak_*_phys() set address type and check PA when scan
In reply to Patrick WangThe kmemleak_*_phys() functions call next level interface by setting address type to physical. And the physical address of objects will be checked for its boundary when scan instead of in kmemleak_*_phys().
--- mm/kmemleak.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-)
mm/kmemleak.c +29 -8
@@ -1156,8 +1156,12 @@ EXPORT_SYMBOL(kmemleak_no_scan);void __ref kmemleak_alloc_phys (phys_addr_t phys,size_t size,int min_count,gfp_t gfp) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_alloc (__va (phys), size, min_count, gfp); +pr_debug ("%s(0x%pa, %zu, %d)\n" , __func__, &phys, size, min_count); + +if (kmemleak_enabled && !min_count) +/* create object with OBJECT_PHYS flag */ +create_object ((unsigned long )phys, size, min_count, + gfp, true); }EXPORT_SYMBOL (kmemleak_alloc_phys);
@@ -1170,8 +1174,10 @@ EXPORT_SYMBOL(kmemleak_alloc_phys);*/ void __ref kmemleak_free_part_phys (phys_addr_t phys,size_t size) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_free_part (__va (phys), size); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +delete_object_part ((unsigned long )phys, size, true); }EXPORT_SYMBOL (kmemleak_free_part_phys);
@@ -1182,8 +1188,10 @@ EXPORT_SYMBOL(kmemleak_free_part_phys);*/ void __ref kmemleak_not_leak_phys (phys_addr_t phys) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_not_leak (__va (phys)); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +make_gray_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_not_leak_phys);
@@ -1194,8 +1202,10 @@ EXPORT_SYMBOL(kmemleak_not_leak_phys);*/ void __ref kmemleak_ignore_phys (phys_addr_t phys) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_ignore (__va (phys)); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +make_black_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_ignore_phys);
@@ -1468,6 +1478,17 @@ static void kmemleak_scan(void) dump_object_info(object); }#endif + +/* ignore objects outside lowmem (paint them black) */ +if ((object->flags &OBJECT_PHYS ) && + !(object->flags &OBJECT_NO_SCAN )) { +unsigned long phys = object->pointer; + +if (PHYS_PFN (phys) < min_low_pfn || +PHYS_PFN (phys + object->size) >= max_low_pfn) +__paint_it (object,KMEMLEAK_BLACK ); + } +/* reset the reference count (whiten the object) */ object->count =0 ;if (color_gray (object) &&get_object (object))
Signature · 1 line
2.25.1
Catalin Marinas
maintainerRe: [PATCH v2 4/4] mm: kmemleak: kmemleak_*_phys() set address type and check PA when scan
Quoted 7 lines · Patrick Wang
mm/kmemleak.c +0
-0@@ -1156,8 +1156,12 @@ EXPORT_SYMBOL(kmemleak_no_scan);void __ref kmemleak_alloc_phys (phys_addr_t phys,size_t size,int min_count,gfp_t gfp)
mm/kmemleak.c EXPORT_SYMBOL(kmemleak_no_scan); · line 1165
Quoted 10 lines
mm/kmemleak.c +6
-2{ -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_alloc (__va (phys), size, min_count, gfp); +pr_debug ("%s(0x%pa, %zu, %d)\n" , __func__, &phys, size, min_count); + +if (kmemleak_enabled && !min_count) +/* create object with OBJECT_PHYS flag */ +create_object ((unsigned long )phys, size, min_count, + gfp, true); }
With an early patch, just drop min_count altogether from this API, assume 0.
Quoted 17 lines
mm/kmemleak.c +4
-2EXPORT_SYMBOL (kmemleak_alloc_phys);@@ -1170,8 +1174,10 @@ EXPORT_SYMBOL(kmemleak_alloc_phys);*/ void __ref kmemleak_free_part_phys (phys_addr_t phys,size_t size) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_free_part (__va (phys), size); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +delete_object_part ((unsigned long )phys, size, true); }EXPORT_SYMBOL (kmemleak_free_part_phys);@@ -1182,8 +1188,10 @@ EXPORT_SYMBOL(kmemleak_free_part_phys);*/
mm/kmemleak.c EXPORT_SYMBOL(kmemleak_free_part_phys); · line 1196
Quoted 10 lines
mm/kmemleak.c +4
-2void __ref kmemleak_not_leak_phys (phys_addr_t phys) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_not_leak (__va (phys)); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +make_gray_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_not_leak_phys);
This function doesn't have any callers, so please remove it.
mm/kmemleak.c EXPORT_SYMBOL(kmemleak_not_leak_phys); · line 1210
Quoted 12 lines
mm/kmemleak.c +4
-2@@ -1194,8 +1202,10 @@ EXPORT_SYMBOL(kmemleak_not_leak_phys);*/ void __ref kmemleak_ignore_phys (phys_addr_t phys) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_ignore (__va (phys)); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +make_black_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_ignore_phys);
Ah, that's still in use and we do need make_black_object(), contrary to what I commented on a previous patch. We can still avoid changing make_gray_object().
(we could replace the kmemleak_ignore_phys() calls kmemleak_free_part_phys() but that's not in line with what we do for the virtual objects)
Quoted 8 lines
mm/kmemleak.c +4
-0@@ -1468,6 +1478,17 @@ static void kmemleak_scan(void) dump_object_info(object); }#endif + +/* ignore objects outside lowmem (paint them black) */ +if ((object->flags &OBJECT_PHYS ) && + !(object->flags &OBJECT_NO_SCAN )) {
mm/kmemleak.c static void kmemleak_scan(void) · line 1494
Quoted 10 lines
mm/kmemleak.c +7
-0+unsigned long phys = object->pointer; + +if (PHYS_PFN (phys) < min_low_pfn || +PHYS_PFN (phys + object->size) >= max_low_pfn) +__paint_it (object,KMEMLEAK_BLACK ); + } +/* reset the reference count (whiten the object) */ object->count =0 ;if (color_gray (object) &&get_object (object))
This looks fine.
Signature · 1 line
Catalin
Re: [PATCH v2 4/4] mm: kmemleak: kmemleak_*_phys() set address type and check PA when scan
Quoted 13 lines · Catalin Marinas
Quoted 12 lines · Patrick Wang
mm/kmemleak.c +2
-2@@ -1156,8 +1156,12 @@ EXPORT_SYMBOL(kmemleak_no_scan);void __ref kmemleak_alloc_phys (phys_addr_t phys,size_t size,int min_count,gfp_t gfp) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_alloc (__va (phys), size, min_count, gfp); +pr_debug ("%s(0x%pa, %zu, %d)\n" , __func__, &phys, size, min_count); +
Quoted 8 lines
mm/kmemleak.c EXPORT_SYMBOL(kmemleak_no_scan); · line 1165
Quoted 5 lines
mm/kmemleak.c +4
-0+if (kmemleak_enabled && !min_count) +/* create object with OBJECT_PHYS flag */ +create_object ((unsigned long )phys, size, min_count, + gfp, true); }With an early patch, just drop
min_countaltogether from this API, assume 0.
Will do.
Quoted 16 lines
Quoted 16 lines
mm/kmemleak.c +4
-2EXPORT_SYMBOL (kmemleak_alloc_phys);@@ -1170,8 +1174,10 @@ EXPORT_SYMBOL(kmemleak_alloc_phys);*/ void __ref kmemleak_free_part_phys (phys_addr_t phys,size_t size) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_free_part (__va (phys), size); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +delete_object_part ((unsigned long )phys, size, true); }EXPORT_SYMBOL (kmemleak_free_part_phys);
Quoted 14 lines
mm/kmemleak.c EXPORT_SYMBOL(kmemleak_free_part_phys); · line 1196
Quoted 12 lines
mm/kmemleak.c +4
-2@@ -1182,8 +1188,10 @@ EXPORT_SYMBOL(kmemleak_free_part_phys);*/ void __ref kmemleak_not_leak_phys (phys_addr_t phys) { -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_not_leak (__va (phys)); +pr_debug ("%s(0x%pa)\n" , __func__, &phys); + +if (kmemleak_enabled) +make_gray_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_not_leak_phys);This function doesn't have any callers, so please remove it.
Will do.
Thanks,
Patrick
Catalin Marinas
maintainerQuoted 7 lines · Patrick Wang
Patrick Wang (4):
mm: kmemleak: addOBJECT_PHYSflag for objects allocated with physical
address
mm: kmemleak: add rbtree for objects allocated with physical address
mm: kmemleak: handle address stored in object based on its type
mm: kmemleak: kmemleak_*_phys() set address type and check PA when
scan
This looks fine at a very quick look but I'll do a in-depth review next week. One more thing needed is to remove the min_count argument to kmemleak_alloc_phys() and assume it's always 0. After this series we can't track them for leaking anyway.
Thanks for putting this together.
Signature · 1 line
Catalin
Quoted 10 lines · Catalin Marinas
Quoted 7 lines · Patrick Wang
Patrick Wang (4):
mm: kmemleak: addOBJECT_PHYSflag for objects allocated with physical
address
mm: kmemleak: add rbtree for objects allocated with physical address
mm: kmemleak: handle address stored in object based on its type
mm: kmemleak: kmemleak_*_phys() set address type and check PA when
scan
Quoted 4 lines
This looks fine at a very quick look but I'll do a in-depth review next week. One more thing needed is to remove the
min_countargument tokmemleak_alloc_phys()and assume it's always 0. After this series we can't track them for leaking anyway.
Will do in the next version.
Thanks,
Patrick
Quoted 5 lines · Patrick Wang
The kmemleak_*_phys() interface uses "
min_low_pfn" and "max_low_pfn" to check address. But on some architectures, kmemleak_*_phys() is called before those two variables initialized. The following steps will be taken:
Quoted 8 lines
1) Add
OBJECT_PHYSflag and rbtree for the objects allocated with physical address
2) Store physical address in objects if allocated withOBJECT_PHYS
3) Check the boundary when scan instead of in kmemleak_*_phys()This patch set will solve:
https://lore.kernel.org/r/[email protected]
https://lore.kernel.org/r/[email protected]
Hi Patrick,
If this patchset fix the above issue, I think we need to add the below fixes tag.
kmemleak_*_phys()")
Thanks.
Quoted 27 lines
v1:
https://lore.kernel.org/r/[email protected]
v1->v2:
- add rbtree for the objects allocated with physical address
- store physical address in objects if allocated withOBJECT_PHYS
- check the upper object boundary as well and avoid duplicate checkPatrick Wang (4):
mm: kmemleak: addOBJECT_PHYSflag for objects allocated with
physical
address
mm: kmemleak: add rbtree for objects allocated with physical
address
mm: kmemleak: handle address stored in object based on its type
mm: kmemleak: kmemleak_*_phys() set address type and check PA when
scanmm/kmemleak.c | 193 ++++++++++++++++++++++++++++++++-------------- ---- 1 file changed, 123 insertions(+), 70 deletions(-)Signature · 1 line
2.25.1
Quoted 17 lines · Kuan-Ying Lee
Quoted 5 lines · Patrick Wang
The kmemleak_*_phys() interface uses "
min_low_pfn" and "max_low_pfn" to check address. But on some architectures, kmemleak_*_phys() is called before those two variables initialized. The following steps will be taken:Quoted 8 lines
1) Add
OBJECT_PHYSflag and rbtree for the objects allocated with physical address
2) Store physical address in objects if allocated withOBJECT_PHYS
3) Check the boundary when scan instead of in kmemleak_*_phys()This patch set will solve:
https://lore.kernel.org/r/[email protected]
Quoted 7 lines
Hi Patrick,
If this patchset fix the above issue, I think we need to add the below fixes tag.
Fixes 23c2d497de21 ("mm: kmemleak: take a full lowmem check inkmemleak_*_phys()")
Hi Kuan-Ying,
Thanks for taking a look.
This series should solve the false positive on ppc32 and arm64. And the false positive on arm64 is triggered by commit 23c2d497de21. So I will add the fixes tag.
Thanks,
Patrick