diff mbox series

[v2] arm: print alloc free paths for address in registers

Message ID 1614239800-27549-1-git-send-email-maninder1.s@samsung.com (mailing list archive)
State New, archived
Headers show
Series [v2] arm: print alloc free paths for address in registers | expand

Commit Message

Maninder Singh Feb. 25, 2021, 7:56 a.m. UTC
In case of "Use After Free" kernel OOPs, free path of object
is required to debug futher.
And in most of cases object address is present in one of registers.

Thus check for register address and if it belongs to slab,
print its alloc and free path.

e.g. in below issue  register r6 belongs to slab, and use after free issue
occurred on one of its derefer values:

[  124.310386] (ptrval)
[  124.312647] 8<--- cut here ---
[  124.313761] Unable to handle kernel paging request at virtual address 6b6b6b6f
[  124.315972] pgd = (ptrval)
...
[  124.328290] pc : [<c052fc0c>]    lr : [<c052fc00>]    psr: 60000013
[  124.330349] sp : c8993d28  ip : 0000bff4  fp : c8ae2020
[  124.332071] r10: 00000000  r9 : 00000001  r8 : c1804cc8
[  124.333803] r7 : 00000000  r6 : c8ae9180  r5 : c1804a80  r4 : c8ae2008
[  124.335936] r3 : 6b6b6b6b  r2 : 315049d6  r1 : 2d867000  r0 : c1396584
..
[  124.365233] register r6: c8ae9180 belongs to slab object
[  124.366364] INFO: Allocated in meminfo_proc_show+0x3c/0x500 age=1 cpu=0 pid=69
[  124.367545]  meminfo_proc_show+0x3c/0x500
[  124.368271]  seq_read_iter+0x10c/0x4bc
[  124.368994]  proc_reg_read_iter+0x74/0xa8
[  124.369712]  generic_file_splice_read+0xe8/0x178
[  124.370496]  splice_direct_to_actor+0xe0/0x2b8
[  124.371261]  do_splice_direct+0xa4/0xdc
[  124.371917]  do_sendfile+0x1c4/0x3ec
[  124.372550]  sys_sendfile64+0x128/0x130
[  124.373109]  ret_fast_syscall+0x0/0x54
[  124.373664]  0xbe9a2de4
[  124.374081] INFO: Freed in meminfo_proc_show+0x5c/0x500 age=1 cpu=0 pid=69
[  124.374933]  meminfo_proc_show+0x5c/0x500
[  124.375485]  seq_read_iter+0x10c/0x4bc
[  124.376020]  proc_reg_read_iter+0x74/0xa8
[  124.376643]  generic_file_splice_read+0xe8/0x178
[  124.377331]  splice_direct_to_actor+0xe0/0x2b8
[  124.378022]  do_splice_direct+0xa4/0xdc
[  124.378633]  do_sendfile+0x1c4/0x3ec
[  124.379220]  sys_sendfile64+0x128/0x130
[  124.379822]  ret_fast_syscall+0x0/0x54
[  124.380421]  0xbe9a2de4

Co-developed-by: Vaneet Narang <v.narang@samsung.com>
Signed-off-by: Vaneet Narang <v.narang@samsung.com>
Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
---
v1 -> v2: do address sanity with virt_addr_valid

 arch/arm/include/asm/bug.h |  1 +
 arch/arm/kernel/process.c  | 18 ++++++++++++++++++
 arch/arm/kernel/traps.c    |  1 +
 include/linux/slab.h       | 14 ++++++++++++++
 mm/slab.h                  |  7 -------
 mm/slub.c                  | 18 ++++++++++++++++++
 6 files changed, 52 insertions(+), 7 deletions(-)

Comments

Maninder Singh March 10, 2021, 5:50 a.m. UTC | #1
Hi,

Any comments or updates?

>Sender : Maninder Singh <maninder1.s@samsung.com> Engineer/Platform S/W Group /SRI-Delhi/Samsung Electronics 
>Date : 2021-02-25 13:57 (GMT+5:30)
>Title : [PATCH v2] arm: print alloc free paths for address in registers
> 
>In case of "Use After Free" kernel OOPs, free path of object
>is required to debug futher.
>And in most of cases object address is present in one of registers.
> 
>Thus check for register address and if it belongs to slab,
>print its alloc and free path.
> 
>e.g. in below issue  register r6 belongs to slab, and use after free issue
>occurred on one of its derefer values:
> 
>[  124.310386] (ptrval)
>[  124.312647] 8<--- cut here ---
>[  124.313761] Unable to handle kernel paging request at virtual address 6b6b6b6f
>[  124.315972] pgd = (ptrval)
>...
>[  124.328290] pc : [<c052fc0c>]    lr : [<c052fc00>]    psr: 60000013
>[  124.330349] sp : c8993d28  ip : 0000bff4  fp : c8ae2020
>[  124.332071] r10: 00000000  r9 : 00000001  r8 : c1804cc8
>[  124.333803] r7 : 00000000  r6 : c8ae9180  r5 : c1804a80  r4 : c8ae2008
>[  124.335936] r3 : 6b6b6b6b  r2 : 315049d6  r1 : 2d867000  r0 : c1396584
>..
>[  124.365233] register r6: c8ae9180 belongs to slab object
>[  124.366364] INFO: Allocated in meminfo_proc_show+0x3c/0x500 age=1 cpu=0 pid=69
>[  124.367545]  meminfo_proc_show+0x3c/0x500
>[  124.368271]  seq_read_iter+0x10c/0x4bc
>[  124.368994]  proc_reg_read_iter+0x74/0xa8
>[  124.369712]  generic_file_splice_read+0xe8/0x178
>[  124.370496]  splice_direct_to_actor+0xe0/0x2b8
>[  124.371261]  do_splice_direct+0xa4/0xdc
>[  124.371917]  do_sendfile+0x1c4/0x3ec
>[  124.372550]  sys_sendfile64+0x128/0x130
>[  124.373109]  ret_fast_syscall+0x0/0x54
>[  124.373664]  0xbe9a2de4
>[  124.374081] INFO: Freed in meminfo_proc_show+0x5c/0x500 age=1 cpu=0 pid=69
>[  124.374933]  meminfo_proc_show+0x5c/0x500
>[  124.375485]  seq_read_iter+0x10c/0x4bc
>[  124.376020]  proc_reg_read_iter+0x74/0xa8
>[  124.376643]  generic_file_splice_read+0xe8/0x178
>[  124.377331]  splice_direct_to_actor+0xe0/0x2b8
>[  124.378022]  do_splice_direct+0xa4/0xdc
>[  124.378633]  do_sendfile+0x1c4/0x3ec
>[  124.379220]  sys_sendfile64+0x128/0x130
>[  124.379822]  ret_fast_syscall+0x0/0x54
>[  124.380421]  0xbe9a2de4
> 
>Co-developed-by: Vaneet Narang <v.narang@samsung.com>
>Signed-off-by: Vaneet Narang <v.narang@samsung.com>
>Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
>---
>v1 -> v2: do address sanity with virt_addr_valid
> 
> arch/arm/include/asm/bug.h |  1 +
> arch/arm/kernel/process.c  | 18 ++++++++++++++++++
> arch/arm/kernel/traps.c    |  1 +
> include/linux/slab.h       | 14 ++++++++++++++
> mm/slab.h                  |  7 -------
> mm/slub.c                  | 18 ++++++++++++++++++
> 6 files changed, 52 insertions(+), 7 deletions(-)
 

Thanks,
Maninder Singh
Vlastimil Babka March 10, 2021, 2:02 p.m. UTC | #2
On 2/25/21 8:56 AM, Maninder Singh wrote:
> In case of "Use After Free" kernel OOPs, free path of object
> is required to debug futher.
> And in most of cases object address is present in one of registers.
> 
> Thus check for register address and if it belongs to slab,
> print its alloc and free path.
> 
> e.g. in below issue  register r6 belongs to slab, and use after free issue
> occurred on one of its derefer values:
> 
> [  124.310386] (ptrval)
> [  124.312647] 8<--- cut here ---
> [  124.313761] Unable to handle kernel paging request at virtual address 6b6b6b6f
> [  124.315972] pgd = (ptrval)
> ...
> [  124.328290] pc : [<c052fc0c>]    lr : [<c052fc00>]    psr: 60000013
> [  124.330349] sp : c8993d28  ip : 0000bff4  fp : c8ae2020
> [  124.332071] r10: 00000000  r9 : 00000001  r8 : c1804cc8
> [  124.333803] r7 : 00000000  r6 : c8ae9180  r5 : c1804a80  r4 : c8ae2008
> [  124.335936] r3 : 6b6b6b6b  r2 : 315049d6  r1 : 2d867000  r0 : c1396584
> ..
> [  124.365233] register r6: c8ae9180 belongs to slab object
> [  124.366364] INFO: Allocated in meminfo_proc_show+0x3c/0x500 age=1 cpu=0 pid=69
> [  124.367545]  meminfo_proc_show+0x3c/0x500
> [  124.368271]  seq_read_iter+0x10c/0x4bc
> [  124.368994]  proc_reg_read_iter+0x74/0xa8
> [  124.369712]  generic_file_splice_read+0xe8/0x178
> [  124.370496]  splice_direct_to_actor+0xe0/0x2b8
> [  124.371261]  do_splice_direct+0xa4/0xdc
> [  124.371917]  do_sendfile+0x1c4/0x3ec
> [  124.372550]  sys_sendfile64+0x128/0x130
> [  124.373109]  ret_fast_syscall+0x0/0x54
> [  124.373664]  0xbe9a2de4
> [  124.374081] INFO: Freed in meminfo_proc_show+0x5c/0x500 age=1 cpu=0 pid=69
> [  124.374933]  meminfo_proc_show+0x5c/0x500
> [  124.375485]  seq_read_iter+0x10c/0x4bc
> [  124.376020]  proc_reg_read_iter+0x74/0xa8
> [  124.376643]  generic_file_splice_read+0xe8/0x178
> [  124.377331]  splice_direct_to_actor+0xe0/0x2b8
> [  124.378022]  do_splice_direct+0xa4/0xdc
> [  124.378633]  do_sendfile+0x1c4/0x3ec
> [  124.379220]  sys_sendfile64+0x128/0x130
> [  124.379822]  ret_fast_syscall+0x0/0x54
> [  124.380421]  0xbe9a2de4
> 
> Co-developed-by: Vaneet Narang <v.narang@samsung.com>
> Signed-off-by: Vaneet Narang <v.narang@samsung.com>
> Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
> ---
> v1 -> v2: do address sanity with virt_addr_valid
> 
>  arch/arm/include/asm/bug.h |  1 +
>  arch/arm/kernel/process.c  | 18 ++++++++++++++++++
>  arch/arm/kernel/traps.c    |  1 +
>  include/linux/slab.h       | 14 ++++++++++++++
>  mm/slab.h                  |  7 -------
>  mm/slub.c                  | 18 ++++++++++++++++++

Instead of your changes to SL*B, could you check mem_dump_obj() and others added
by Paul in 5.12-rc1?

(+CC Paul, thus not trimming)

Thanks,
Vlastimil

>  6 files changed, 52 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm/include/asm/bug.h b/arch/arm/include/asm/bug.h
> index 673c7dd..ba8d9d7 100644
> --- a/arch/arm/include/asm/bug.h
> +++ b/arch/arm/include/asm/bug.h
> @@ -88,5 +88,6 @@ extern asmlinkage void c_backtrace(unsigned long fp, int pmode,
>  struct mm_struct;
>  void show_pte(const char *lvl, struct mm_struct *mm, unsigned long addr);
>  extern void __show_regs(struct pt_regs *);
> +extern void __show_regs_alloc_free(struct pt_regs *regs);
>  
>  #endif
> diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
> index 5199a2b..97d2a7c 100644
> --- a/arch/arm/kernel/process.c
> +++ b/arch/arm/kernel/process.c
> @@ -27,6 +27,7 @@
>  #include <linux/random.h>
>  #include <linux/hw_breakpoint.h>
>  #include <linux/leds.h>
> +#include <linux/slab.h>
>  
>  #include <asm/processor.h>
>  #include <asm/thread_notify.h>
> @@ -92,6 +93,23 @@ void arch_cpu_idle_exit(void)
>  	ledtrig_cpu(CPU_LED_IDLE_END);
>  }
>  
> +void __show_regs_alloc_free(struct pt_regs *regs)
> +{
> +	int i;
> +
> +	/* check for r0 - r12 only */
> +	for (i = 0; i < 13; i++) {
> +		unsigned long addr = regs->uregs[i];
> +		void *object;
> +		struct kmem_cache *cache;
> +
> +		if (slab_page_object(addr, &object, &cache)) {
> +			printk("\nregister r%d: %lx belongs to slab object\n", i, addr);
> +			print_tracking(cache, object);
> +		}
> +	}
> +}
> +
>  void __show_regs(struct pt_regs *regs)
>  {
>  	unsigned long flags;
> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
> index 17d5a78..64308e3 100644
> --- a/arch/arm/kernel/traps.c
> +++ b/arch/arm/kernel/traps.c
> @@ -287,6 +287,7 @@ static int __die(const char *str, int err, struct pt_regs *regs)
>  
>  	print_modules();
>  	__show_regs(regs);
> +	__show_regs_alloc_free(regs);
>  	pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
>  		 TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), end_of_stack(tsk));
>  
> diff --git a/include/linux/slab.h b/include/linux/slab.h
> index 7ae6040..a19ba55 100644
> --- a/include/linux/slab.h
> +++ b/include/linux/slab.h
> @@ -706,4 +706,18 @@ static inline void *kzalloc_node(size_t size, gfp_t flags, int node)
>  #define slab_dead_cpu		NULL
>  #endif
>  
> +#ifdef CONFIG_SLUB_DEBUG
> +bool slab_page_object(unsigned long address, void **object, struct kmem_cache **cache);
> +extern void print_tracking(struct kmem_cache *s, void *object);
> +#else
> +static inline void print_tracking(struct kmem_cache *s, void *object)
> +{
> +}
> +
> +static inline bool slab_page_object(unsigned long address, void **object, struct kmem_cache **cache)
> +{
> +	return false;
> +}
> +#endif
> +
>  #endif	/* _LINUX_SLAB_H */
> diff --git a/mm/slab.h b/mm/slab.h
> index 076582f..8a072bd 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -208,18 +208,11 @@ static inline enum node_stat_item cache_vmstat_idx(struct kmem_cache *s)
>  		NR_SLAB_RECLAIMABLE_B : NR_SLAB_UNRECLAIMABLE_B;
>  }
>  
> -#ifdef CONFIG_SLUB_DEBUG
>  #ifdef CONFIG_SLUB_DEBUG_ON
>  DECLARE_STATIC_KEY_TRUE(slub_debug_enabled);
>  #else
>  DECLARE_STATIC_KEY_FALSE(slub_debug_enabled);
>  #endif
> -extern void print_tracking(struct kmem_cache *s, void *object);
> -#else
> -static inline void print_tracking(struct kmem_cache *s, void *object)
> -{
> -}
> -#endif
>  
>  /*
>   * Returns true if any of the specified slub_debug flags is enabled for the
> diff --git a/mm/slub.c b/mm/slub.c
> index 0d5fac3..31436db 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -648,6 +648,24 @@ void print_tracking(struct kmem_cache *s, void *object)
>  	print_track("Freed", get_track(s, object, TRACK_FREE), pr_time);
>  }
>  
> +bool slab_page_object(unsigned long address, void **object, struct kmem_cache **cache)
> +{
> +	void *addr = (void *)address;
> +	struct page *page;
> +
> +	if (virt_addr_valid(addr)) {
> +		page = virt_to_head_page(addr);
> +
> +		if (PageSlab(page)) {
> +			*cache = page->slab_cache;
> +			*object = nearest_obj(*cache, page, addr);
> +			return true;
> +		}
> +	}
> +
> +	return false;
> +}
> +
>  static void print_page_info(struct page *page)
>  {
>  	pr_err("INFO: Slab 0x%p objects=%u used=%u fp=0x%p flags=0x%04lx\n",
>
Vlastimil Babka March 12, 2021, 9:21 a.m. UTC | #3
On 3/11/21 11:51 AM, Maninder Singh wrote:
> Hi,
> 
>  
> 
>> Instead of your changes to SL*B, could you check mem_dump_obj() and others added
>> by Paul in 5.12-rc1?
> 
>> (+CC Paul, thus not trimming)
> 
>  
> 
> checked mem_dump_obj(), but it only provides path of allocation and not of free.
> 
> /**
>  * mem_dump_obj - Print available provenance information
> 
> ..
> 
>  * if available, the return address and stack trace from the allocation
>  * of that object.
>  */
> void mem_dump_obj(void *object)
> 
> and in case of "Use After Free", Free path is also required.
> 
> So we need to add support for free path in this API if we have to use it.

I think that would make sense.  

> Thanks,
> 
> Maninder Singh
>
diff mbox series

Patch

diff --git a/arch/arm/include/asm/bug.h b/arch/arm/include/asm/bug.h
index 673c7dd..ba8d9d7 100644
--- a/arch/arm/include/asm/bug.h
+++ b/arch/arm/include/asm/bug.h
@@ -88,5 +88,6 @@  extern asmlinkage void c_backtrace(unsigned long fp, int pmode,
 struct mm_struct;
 void show_pte(const char *lvl, struct mm_struct *mm, unsigned long addr);
 extern void __show_regs(struct pt_regs *);
+extern void __show_regs_alloc_free(struct pt_regs *regs);
 
 #endif
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 5199a2b..97d2a7c 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -27,6 +27,7 @@ 
 #include <linux/random.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/leds.h>
+#include <linux/slab.h>
 
 #include <asm/processor.h>
 #include <asm/thread_notify.h>
@@ -92,6 +93,23 @@  void arch_cpu_idle_exit(void)
 	ledtrig_cpu(CPU_LED_IDLE_END);
 }
 
+void __show_regs_alloc_free(struct pt_regs *regs)
+{
+	int i;
+
+	/* check for r0 - r12 only */
+	for (i = 0; i < 13; i++) {
+		unsigned long addr = regs->uregs[i];
+		void *object;
+		struct kmem_cache *cache;
+
+		if (slab_page_object(addr, &object, &cache)) {
+			printk("\nregister r%d: %lx belongs to slab object\n", i, addr);
+			print_tracking(cache, object);
+		}
+	}
+}
+
 void __show_regs(struct pt_regs *regs)
 {
 	unsigned long flags;
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 17d5a78..64308e3 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -287,6 +287,7 @@  static int __die(const char *str, int err, struct pt_regs *regs)
 
 	print_modules();
 	__show_regs(regs);
+	__show_regs_alloc_free(regs);
 	pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
 		 TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), end_of_stack(tsk));
 
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 7ae6040..a19ba55 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -706,4 +706,18 @@  static inline void *kzalloc_node(size_t size, gfp_t flags, int node)
 #define slab_dead_cpu		NULL
 #endif
 
+#ifdef CONFIG_SLUB_DEBUG
+bool slab_page_object(unsigned long address, void **object, struct kmem_cache **cache);
+extern void print_tracking(struct kmem_cache *s, void *object);
+#else
+static inline void print_tracking(struct kmem_cache *s, void *object)
+{
+}
+
+static inline bool slab_page_object(unsigned long address, void **object, struct kmem_cache **cache)
+{
+	return false;
+}
+#endif
+
 #endif	/* _LINUX_SLAB_H */
diff --git a/mm/slab.h b/mm/slab.h
index 076582f..8a072bd 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -208,18 +208,11 @@  static inline enum node_stat_item cache_vmstat_idx(struct kmem_cache *s)
 		NR_SLAB_RECLAIMABLE_B : NR_SLAB_UNRECLAIMABLE_B;
 }
 
-#ifdef CONFIG_SLUB_DEBUG
 #ifdef CONFIG_SLUB_DEBUG_ON
 DECLARE_STATIC_KEY_TRUE(slub_debug_enabled);
 #else
 DECLARE_STATIC_KEY_FALSE(slub_debug_enabled);
 #endif
-extern void print_tracking(struct kmem_cache *s, void *object);
-#else
-static inline void print_tracking(struct kmem_cache *s, void *object)
-{
-}
-#endif
 
 /*
  * Returns true if any of the specified slub_debug flags is enabled for the
diff --git a/mm/slub.c b/mm/slub.c
index 0d5fac3..31436db 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -648,6 +648,24 @@  void print_tracking(struct kmem_cache *s, void *object)
 	print_track("Freed", get_track(s, object, TRACK_FREE), pr_time);
 }
 
+bool slab_page_object(unsigned long address, void **object, struct kmem_cache **cache)
+{
+	void *addr = (void *)address;
+	struct page *page;
+
+	if (virt_addr_valid(addr)) {
+		page = virt_to_head_page(addr);
+
+		if (PageSlab(page)) {
+			*cache = page->slab_cache;
+			*object = nearest_obj(*cache, page, addr);
+			return true;
+		}
+	}
+
+	return false;
+}
+
 static void print_page_info(struct page *page)
 {
 	pr_err("INFO: Slab 0x%p objects=%u used=%u fp=0x%p flags=0x%04lx\n",