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: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20250818122720.434981-14-wangjinchao600@gmail.com>
Date: Mon, 18 Aug 2025 20:26:18 +0800
From: Jinchao Wang <wangjinchao600@...il.com>
To: akpm@...ux-foundation.org
Cc: mhiramat@...nel.org,
	naveen@...nel.org,
	davem@...emloft.net,
	linux-mm@...ck.org,
	linux-kernel@...r.kernel.org,
	linux-trace-kernel@...r.kernel.org,
	Jinchao Wang <wangjinchao600@...il.com>
Subject: [RFC PATCH 13/13] mm/kstackwatch: Add a test module and script

This patch introduces a test module and a helper script to verify the
functionality of the KStackWatch debugging tool.

The `kstackwatch_test.c` module provides a set of controlled scenarios for
stack-related issues, including:
- Direct write using ksw_watch_fire().
- Intentional stack overflow.
- Corruption of a local variable from another thread.
- Recursive function calls to test nested stack frames.

The accompanying `kstackwatch_test.sh` script automates the process of
loading the test module, configuring the KStackWatch tool via its debugfs
interface, and triggering each test case. This script also includes
instructions for users to find the necessary offsets for their specific
kernel build.

This dedicated test module and script significantly aid in the development,
validation, and long-term maintenance of the KStackWatch tool.

Signed-off-by: Jinchao Wang <wangjinchao600@...il.com>
---
 mm/Kconfig.debug                      |  11 ++
 mm/kstackwatch/Makefile               |   8 +
 mm/kstackwatch/kstackwatch_test.c     | 237 ++++++++++++++++++++++++++
 tools/kstackwatch/kstackwatch_test.sh | 122 +++++++++++++
 4 files changed, 378 insertions(+)
 create mode 100644 mm/kstackwatch/kstackwatch_test.c
 create mode 100644 tools/kstackwatch/kstackwatch_test.sh

diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
index dd9c1bb7f549..5e9402e39001 100644
--- a/mm/Kconfig.debug
+++ b/mm/Kconfig.debug
@@ -321,3 +321,14 @@ config KSTACK_WATCH
 	  which is useful for debugging stability issues.
 
 	  If unsure, say N.
+
+
+config KSTACK_WATCH_TEST
+	tristate "KStackWatch Test Module"
+	depends on KSTACK_WATCH
+	help
+	  This module provides controlled stack exhaustion and overflow scenarios
+	  to verify the functionality of KStackWatch. It is particularly useful
+	  for development and validation of the KStachWatch mechanism.
+
+	  If unsure, say N.
diff --git a/mm/kstackwatch/Makefile b/mm/kstackwatch/Makefile
index 076822eb7661..5e63e8c2e7c0 100644
--- a/mm/kstackwatch/Makefile
+++ b/mm/kstackwatch/Makefile
@@ -1,3 +1,11 @@
 obj-$(CONFIG_KSTACK_WATCH)	+= kstackwatch.o
 
 kstackwatch-y := kernel.o stack.o watch.o
+
+obj-$(CONFIG_KSTACK_WATCH_TEST)	+= kstackwatch_test.o
+
+kstackwatch-y := kernel.o stack.o watch.o
+CFLAGS_kstackwatch_test.o := -fno-ipa-sra -fno-inline \
+                             -fno-optimize-sibling-calls \
+                             -fno-section-anchors \
+                             -fno-pic -fno-pie
diff --git a/mm/kstackwatch/kstackwatch_test.c b/mm/kstackwatch/kstackwatch_test.c
new file mode 100644
index 000000000000..c21618ca0c8b
--- /dev/null
+++ b/mm/kstackwatch/kstackwatch_test.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+
+#include "kstackwatch.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jinchao Wang");
+MODULE_DESCRIPTION("Simplified KStackWatch Test Module ");
+
+static struct proc_dir_entry *test_proc;
+#define BUFFER_SIZE 4
+#define MAX_DEPTH 4
+
+/* Global variables for multi-thread test synchronization */
+static u64 *g_corrupt_ptr;
+static struct completion g_wait_for_init;
+
+/*
+ * Test Case 0: Write to the canary position directly (Canary Test)
+ * This function uses a u64 buffer
+ * 64-bit write, corrupting the stack canary with a single operation.
+ */
+static void canary_test_write(void)
+{
+	u64 buffer[BUFFER_SIZE];
+
+	pr_info("KStackWatch Test: Starting %s with u64 write\n", __func__);
+	ksw_watch_show();
+	ksw_watch_fire();
+
+	buffer[0] = 0;
+	/* make sure the compiler do not drop assign action */
+	barrier_data(buffer);
+	pr_info("KStackWatch Test: Canary write test completed\n");
+}
+
+/*
+ * Test Case 1: Stack Overflow (Canary Test)
+ * This function uses a u64 buffer
+ * 64-bit write, corrupting the stack canary with a single operation.
+ */
+static void canary_test_overflow(void)
+{
+	u64 buffer[BUFFER_SIZE];
+
+	pr_info("KStackWatch Test: Starting %s with u64 write\n", __func__);
+	pr_info("KStackWatch Test: buffer 0x%px\n", buffer);
+
+	/* Intentionally overflow the u64 buffer. */
+	buffer[BUFFER_SIZE] = 0xdeadbeefdeadbeef;
+
+	/* make sure the compiler do not drop assign action */
+	barrier_data(buffer);
+
+	pr_info("KStackWatch Test: Canary overflow test completed\n");
+}
+
+/*
+ * Corrupts the variable on multi_thread_corruption_test's stack
+ */
+static int multi_thread_corruption_thread2(void *data)
+{
+	pr_info("KStackWatch Test: Starting %s\n", __func__);
+
+	wait_for_completion(&g_wait_for_init);
+
+	pr_info("KStackWatch Test: Thread2  woke up. Corrupting variable at 0x%px\n",
+		g_corrupt_ptr);
+	if (g_corrupt_ptr)
+		*g_corrupt_ptr = 0xdeadbeefdeadbeef;
+
+	pr_info("KStackWatch Test: Thread2 finished corruption\n");
+
+	return 0;
+}
+
+/*
+ * Test Case 2: Multi-threaded Local Variable Corruption
+ * multi_thread_corruption_test initializes a local variable
+ * makes its address globally available,
+ * and then sleeps. Thread B corrupts it.
+ */
+static void multi_thread_corruption_thread1(void)
+{
+	u64 local_var = 0x1234567887654321;
+
+	pr_info("KStackWatch Test: Starting %s\n", __func__);
+
+	pr_info("KStackWatch Test: Thread1 local_var address: 0x%px, value: 0x%llx\n",
+		&local_var, local_var);
+	WRITE_ONCE(g_corrupt_ptr, &local_var);
+
+	/* Signal Thread 2 that the pointer is ready, then sleep */
+	complete(&g_wait_for_init);
+	msleep(1000);
+
+	pr_info("KStackWatch Test: Thread1 woke up. Final local_var value: 0x%llx\n",
+		local_var);
+}
+
+static void multi_thread_corruption_test(void)
+{
+	struct task_struct *corrupt_thread;
+
+	pr_info("KStackWatch Test: Starting %s\n", __func__);
+
+	/* Reset completion */
+	init_completion(&g_wait_for_init);
+
+	corrupt_thread = kthread_run(multi_thread_corruption_thread2, NULL,
+				     "corruption_b");
+	if (IS_ERR(corrupt_thread)) {
+		pr_err("KStackWatch Test: Failed to create corruption thread\n");
+		return;
+	}
+	multi_thread_corruption_thread1();
+}
+
+/*
+ * Test Case 3: Recursive Call Corruption
+ * This function calls itself recursively and corrupts the stack at a specific depth.
+ * This tests whether KStackWatch can handle dynamic stack frames.
+ */
+static void recursive_corruption_test(int depth)
+{
+	u64 buffer[BUFFER_SIZE];
+
+	pr_info("KStackWatch Test: Recursive call at depth %d\n", depth);
+	pr_info("KStackWatch Test: buffer 0x%px\n", buffer);
+	if (depth <= MAX_DEPTH)
+		recursive_corruption_test(depth + 1);
+
+	buffer[0] = depth;
+
+	/* make sure the compiler do not drop assign action */
+	barrier_data(buffer);
+
+	pr_info("KStackWatch Test: Returning from depth %d\n", depth);
+}
+
+static ssize_t test_proc_write(struct file *file, const char __user *buffer,
+			       size_t count, loff_t *pos)
+{
+	char cmd[256];
+	int test_num;
+
+	if (count >= sizeof(cmd))
+		return -EINVAL;
+
+	if (copy_from_user(cmd, buffer, count))
+		return -EFAULT;
+
+	cmd[count] = '\0';
+	strim(cmd);
+
+	pr_info("KStackWatch Test: Received command: %s\n", cmd);
+
+	if (sscanf(cmd, "test%d", &test_num) == 1) {
+		switch (test_num) {
+		case 0:
+			pr_info("KStackWatch Test: Triggering canary write test\n");
+			canary_test_write();
+			break;
+		case 1:
+			pr_info("KStackWatch Test: Triggering canary overflow test\n");
+			canary_test_overflow();
+			break;
+		case 2:
+			pr_info("KStackWatch Test: Triggering local variable corruption test\n");
+			multi_thread_corruption_test();
+			break;
+		case 3:
+			pr_info("KStackWatch Test: Triggering recursive corruption test\n");
+			/* depth start with 0 */
+			recursive_corruption_test(0);
+			break;
+		default:
+			pr_err("KStackWatch Test: Unknown test number %d\n",
+			       test_num);
+			return -EINVAL;
+		}
+	} else {
+		pr_err("KStackWatch Test: Invalid command format. Use 'test1', 'test2', or 'test3'.\n");
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+static ssize_t test_proc_read(struct file *file, char __user *buffer,
+			      size_t count, loff_t *pos)
+{
+	static const char usage[] =
+		"KStackWatch Simplified Test Module\n"
+		"==================================\n"
+		"Usage:\n"
+		"  echo 'test0' > /proc/kstackwatch_test  - Canary test test\n"
+		"  echo 'test1' > /proc/kstackwatch_test  - Canary overflow test\n"
+		"  echo 'test2' > /proc/kstackwatch_test  - Multi-threaded local variable corruption test\n"
+		"  echo 'test3' > /proc/kstackwatch_test  - Recursive corruption test\n";
+
+	return simple_read_from_buffer(buffer, count, pos, usage,
+				       strlen(usage));
+}
+
+static const struct proc_ops test_proc_ops = {
+	.proc_read = test_proc_read,
+	.proc_write = test_proc_write,
+};
+
+static int __init kstackwatch_test_init(void)
+{
+	test_proc = proc_create("kstackwatch_test", 0644, NULL, &test_proc_ops);
+	if (!test_proc) {
+		pr_err("KStackWatch Test: Failed to create proc entry\n");
+		return -ENOMEM;
+	}
+	pr_info("KStackWatch Test: Module loaded, use 'cat /proc/kstackwatch_test' for usage\n");
+	return 0;
+}
+
+static void __exit kstackwatch_test_exit(void)
+{
+	if (test_proc)
+		remove_proc_entry("kstackwatch_test", NULL);
+	pr_info("KStackWatch Test: Module unloaded\n");
+}
+
+module_init(kstackwatch_test_init);
+module_exit(kstackwatch_test_exit);
diff --git a/tools/kstackwatch/kstackwatch_test.sh b/tools/kstackwatch/kstackwatch_test.sh
new file mode 100644
index 000000000000..33ca590b7374
--- /dev/null
+++ b/tools/kstackwatch/kstackwatch_test.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+# --- Usage function ---
+usage() {
+    echo "======================================"
+    echo "  KStackWatch Test Script Usage"
+    echo "======================================"
+    echo ""
+    echo "IMPORTANT: Before running, make sure you have updated the offset values!"
+    echo ""
+    echo "To find your offsets, use objdump:"
+    echo "  objdump -S --disassemble=canary_test_write vmlinux"
+    echo ""
+    echo "Then search for your function names to find the instruction addresses."
+    echo "- Instruction offset: address relative to function's start"
+    echo "- Stack var offset: distance from stack base (%rbp) to the variable"
+    echo ""
+    echo "Usage: $0 [test_case_number]"
+    echo ""
+    echo "Available test cases:"
+    echo "  0  - Canary Write Test"
+    echo "  1  - Canary Overflow Test"
+    echo "  2  - Multi-threaded Local Variable Corruption Test"
+    echo "  3  - Recursive Corruption Test"
+    echo ""
+    echo "======================================"
+    echo ""
+}
+
+# --- Interactive menu ---
+show_menu() {
+    echo "Select a test case to run:"
+    echo "  0) Canary Write Test"
+    echo "  1) Canary Overflow Test"
+    echo "  2) Multi-threaded Local Variable Corruption Test"
+    echo "  3) Recursive Corruption Test"
+    echo "  q) Quit"
+    echo ""
+    echo "WARNING: Each test may cause system crash/hang!"
+    echo ""
+    read -p "Enter your choice [0-3/q]: " choice
+    echo ""
+
+    case "$choice" in
+        0) test0 ;;
+        1) test1 ;;
+        2) test2 ;;
+        3) test3 ;;
+        q|Q) echo "Exiting..."; exit 0 ;;
+        *) echo "Invalid choice. Please try again."; echo ""; show_menu ;;
+    esac
+}
+
+# --- Test Case 0: Canary Write ---
+test0() {
+    echo "=== Running Test Case 0: Canary Write ==="
+    # function+instruction_off[+depth] [local_var_offset:local_var_len]
+    echo "canary_test_write+0x19" > /proc/kstackwatch
+    echo "test0" > /proc/kstackwatch_test
+    echo ""
+    echo "-------------------------------------"
+    echo ""
+}
+
+# --- Test Case 1: Canary Overflow ---
+test1() {
+    echo "=== Running Test Case 1: Canary Overflow ==="
+    # function+instruction_off[+depth] [local_var_offset:local_var_len]
+    echo "canary_test_overflow+0x19" > /proc/kstackwatch
+    echo "test1" > /proc/kstackwatch_test
+    echo ""
+    echo "-------------------------------------"
+    echo ""
+}
+
+# --- Test Case 2: Multi-threaded Local Variable Corruption ---
+test2() {
+    echo "=== Running Test Case 2: Multi-threaded Corruption ==="
+    # function+instruction_off[+depth] [local_var_offset:local_var_len]
+    echo "multi_thread_corruption_thread1+0x2b 0:8" > /proc/kstackwatch
+    echo "test2" > /proc/kstackwatch_test
+    echo ""
+    echo "-------------------------------------"
+    echo ""
+}
+
+# --- Test Case 3: Recursive Corruption ---
+test3() {
+    echo "=== Running Test Case 3: Recursive Corruption ==="
+    # function+instruction_off[+depth] [local_var_offset:local_var_len]
+    echo "recursive_corruption_test+0x2b+3 0:8"  > /proc/kstackwatch
+    echo "test3" > /proc/kstackwatch_test
+    echo ""
+    echo "-------------------------------------"
+    echo ""
+}
+
+# --- Run all tests ---
+# Removed: These tests cause system crashes and should not be run sequentially
+
+echo 7 > /proc/sys/kernel/printk
+
+# --- Main ---
+if [ -z "$1" ]; then
+    usage
+    echo ""
+    show_menu
+else
+    case "$1" in
+        0) test0 ;;
+        1) test1 ;;
+        2) test2 ;;
+        3) test3 ;;
+        help|--help|-h) usage ;;
+        *)
+            echo "Error: Invalid argument '$1'"
+            echo ""
+            usage
+            exit 1
+            ;;
+    esac
+fi
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ