[PATCH v2 09/18] mm/ksw: implement stack canary and local var resolution logic

Jinchao Wang
2025-09-04T00:23:14Z

Implement logic to resolve stack watch target for kstackwatch:
- Locate the stack canary within the current frame
- Resolve local variable offsets relative to the stack pointer
- Validate addresses against current task's stack bounds

This logic prepares watch addr and len for use in kprobe/fprobe handlers,
enabling dynamic stack monitoring.

Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
---
mm/kstackwatch/stack.c | 99 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 96 insertions(+), 3 deletions(-)

diff --git a/mm/kstackwatch/stack.c b/mm/kstackwatch/stack.c
index 3b72177315cc..00cb38085a9f 100644
--- a/mm/kstackwatch/stack.c
+++ b/mm/kstackwatch/stack.c
@@ -1,22 +1,115 @@
// SPDX-License-Identifier: GPL-2.0

#include <linux/fprobe.h>
+#include <linux/interrupt.h>
#include <linux/kprobes.h>
+#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
+#include <linux/stackprotector.h>

#include "kstackwatch.h"

struct ksw_config *probe_config;

+/* Find canary address in current stack frame */
+static unsigned long ksw_stack_find_canary(struct pt_regs *regs)
+{
+ unsigned long *stack_ptr, *stack_end;
+ unsigned long expected_canary;
+ unsigned int i;
+
+ stack_ptr = (unsigned long *)kernel_stack_pointer(regs);
+ stack_end =
+ (unsigned long *)current->stack + THREAD_SIZE / sizeof(long);
+ expected_canary = current->stack_canary;
+
+ for (i = 0; i < MAX_FRAME_SEARCH && &stack_ptr[i] < stack_end; i++) {
+ if (stack_ptr[i] == expected_canary) {
+ pr_info("KSW: canary found i:%d 0x%px\n", i,
+ &stack_ptr[i]);
+ return (unsigned long)&stack_ptr[i];
+ }
+ }
+
+ return 0;
+}
+
+/* Resolve stack offset to actual address */
+static unsigned long ksw_stack_resolve_offset(struct pt_regs *regs,
+ s64 local_var_offset)
+{
+ unsigned long stack_base;
+ unsigned long target_addr;
+
+ if (!regs)
+ return 0;
+
+ /* Use stack pointer as base for offset calculation */
+ stack_base = kernel_stack_pointer(regs);
+ target_addr = stack_base + local_var_offset;
+
+ pr_debug("KSW: stack resolve offset target: 0x%lx\n", target_addr);
+
+ return target_addr;
+}
+
+/* Validate that address is within current stack bounds */
+static int ksw_stack_validate_addr(unsigned long addr, size_t size)
+{
+ unsigned long stack_start, stack_end;
+
+ if (!addr || !size)
+ return -EINVAL;
+
+ stack_start = (unsigned long)current->stack;
+ stack_end = stack_start + THREAD_SIZE;
+
+ if (addr < stack_start || (addr + size) > stack_end) {
+ pr_warn("KSW: address 0x%lx (size %zu) outside stack bounds [0x%lx-0x%lx]\n",
+ addr, size, stack_start, stack_end);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
/* prepare watch_addr and watch_len for watch */
static int ksw_stack_prepare_watch(struct pt_regs *regs,
struct ksw_config *config, u64 *watch_addr,
u64 *watch_len)
{
- /* TODO: implement logic */
- *watch_addr = 0;
- *watch_len = 0;
+ u64 addr;
+ u64 len;
+
+ /* Resolve addresses for all active watches */
+ switch (config->type) {
+ case WATCH_CANARY:
+ addr = ksw_stack_find_canary(regs);
+ len = 8;
+ break;
+
+ case WATCH_LOCAL_VAR:
+ addr = ksw_stack_resolve_offset(regs, config->local_var_offset);
+ if (!addr) {
+ pr_err("KSW: invalid stack var offset %u\n",
+ config->local_var_offset);
+ return -EINVAL;
+ }
+ if (ksw_stack_validate_addr(addr, config->local_var_len)) {
+ pr_err("KSW: invalid stack var len %u\n",
+ config->local_var_len);
+ }
+ len = config->local_var_len;
+ break;
+
+ default:
+ pr_warn("KSW: Unknown watch type %d\n", config->type);
+ return -EINVAL;
+ }
+
+ *watch_addr = addr;
+ *watch_len = len;
return 0;
}

--
2.43.0