lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20241017214251.170602-1-niharchaithanya@gmail.com>
Date: Fri, 18 Oct 2024 03:12:54 +0530
From: Nihar Chaithanya <niharchaithanya@...il.com>
To: ryabinin.a.a@...il.com
Cc: andreyknvl@...il.com,
	dvyukov@...gle.com,
	skhan@...uxfoundation.org,
	kasan-dev@...glegroups.com,
	linux-kernel@...r.kernel.org,
	Nihar Chaithanya <niharchaithanya@...il.com>
Subject: [PATCH] kasan:report: filter out kasan related stack entries

The reports of KASAN include KASAN related stack frames which are not
the point of interest in the stack-trace. KCSAN report filters out such
internal frames providing relevant stack trace. Currently, KASAN reports
are generated by dump_stack_lvl() which prints the entire stack.

Add functionality to KASAN reports to save the stack entries and filter
out the kasan related stack frames in place of dump_stack_lvl().

Within this new functionality:
	- A function save_stack_lvl_kasan() in place of dump_stack_lvl() is
	  created which contains functionality for saving, filtering and printing
          the stack-trace.
	- The stack-trace is saved to an array using stack_trace_save() similar to
  	  KCSAN reporting which is useful for filtering the stack-trace,
	- The sanitize_stack_entries() function is included to get the number of
          entries to be skipped for filtering similar to KCSAN reporting,
	- The dump_stack_print_info() which prints generic debug info is included
  	  from __dump_stack(),
	- And the function print_stack_trace() to print the stack-trace using the
 	  array containing stack entries as well as the number of entries to be
 	  skipped or filtered out is included.

Signed-off-by: Nihar Chaithanya <niharchaithanya@...il.com>
Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=215756
---
 mm/kasan/report.c | 92 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 90 insertions(+), 2 deletions(-)

diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index b48c768acc84..c180cd8b32ae 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -39,6 +39,7 @@ static unsigned long kasan_flags;
 
 #define KASAN_BIT_REPORTED	0
 #define KASAN_BIT_MULTI_SHOT	1
+#define NUM_STACK_ENTRIES 64
 
 enum kasan_arg_fault {
 	KASAN_ARG_FAULT_DEFAULT,
@@ -369,12 +370,99 @@ static inline bool init_task_stack_addr(const void *addr)
 			sizeof(init_thread_union.stack));
 }
 
+/* Helper to skip KASAN-related functions in stack-trace. */
+static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries)
+{
+	char buf[64];
+	int len, skip;
+
+	for (skip = 0; skip < num_entries; ++skip) {
+		len = scnprintf(buf, sizeof(buf), "%ps", (void *)stack_entries[skip]);
+
+		/* Never show  kasan_* functions. */
+		if (strnstr(buf, "kasan_", len) == buf)
+			continue;
+		/*
+		 * No match for runtime functions -- @skip entries to skip to
+		 * get to first frame of interest.
+		 */
+		break;
+	}
+
+	return skip;
+}
+
+static int
+replace_stack_entry(unsigned long stack_entries[], int num_entries, unsigned long ip,
+		    unsigned long *replaced)
+{
+	unsigned long symbolsize, offset;
+	unsigned long target_func;
+	int skip;
+
+	if (kallsyms_lookup_size_offset(ip, &symbolsize, &offset))
+		target_func = ip - offset;
+	else
+		goto fallback;
+
+	for (skip = 0; skip < num_entries; ++skip) {
+		unsigned long func = stack_entries[skip];
+
+		if (!kallsyms_lookup_size_offset(func, &symbolsize, &offset))
+			goto fallback;
+		func -= offset;
+
+		if (func == target_func) {
+			*replaced = stack_entries[skip];
+			stack_entries[skip] = ip;
+			return skip;
+		}
+	}
+
+fallback:
+	/* Should not happen; the resulting stack trace is likely misleading. */
+	WARN_ONCE(1, "Cannot find frame for %pS in stack trace", (void *)ip);
+	return get_stack_skipnr(stack_entries, num_entries);
+}
+
+static void
+print_stack_trace(unsigned long stack_entries[], int num_entries, unsigned long reordered_to)
+{
+	stack_trace_print(stack_entries, num_entries, 0);
+	if (reordered_to)
+		pr_err("  |\n  +-> reordered to: %pS\n", (void *)reordered_to);
+}
+
+static int
+sanitize_stack_entries(unsigned long stack_entries[], int num_entries, unsigned long ip,
+		       unsigned long *replaced)
+{
+	return ip ? replace_stack_entry(stack_entries, num_entries, ip, replaced) :
+			  get_stack_skipnr(stack_entries, num_entries);
+}
+
+static void save_stack_lvl_kasan(const char *log_lvl, struct kasan_report_info *info)
+{
+	unsigned long reordered_to = 0;
+	unsigned long stack_entries[NUM_STACK_ENTRIES] = {0};
+	int num_stack_entries = stack_trace_save(stack_entries, NUM_STACK_ENTRIES, 1);
+	int skipnr = sanitize_stack_entries(stack_entries,
+				 num_stack_entries, info->ip, &reordered_to);
+
+	dump_stack_print_info(log_lvl);
+	pr_err("\n");
+
+	print_stack_trace(stack_entries + skipnr, num_stack_entries - skipnr,
+					 reordered_to);
+	pr_err("\n");
+}
+
 static void print_address_description(void *addr, u8 tag,
 				      struct kasan_report_info *info)
 {
 	struct page *page = addr_to_page(addr);
 
-	dump_stack_lvl(KERN_ERR);
+	save_stack_lvl_kasan(KERN_ERR, info);
 	pr_err("\n");
 
 	if (info->cache && info->object) {
@@ -488,7 +576,7 @@ static void print_report(struct kasan_report_info *info)
 		print_address_description(addr, tag, info);
 		print_memory_metadata(info->first_bad_addr);
 	} else {
-		dump_stack_lvl(KERN_ERR);
+		save_stack_lvl_kasan(KERN_ERR, info);
 	}
 }
 
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ