From 173b6f3a0328afa59ebed63ef901b0c0d4a8c210 Mon Sep 17 00:00:00 2001 From: Ron Sobol Date: Sun, 30 Mar 2025 13:18:54 +0300 Subject: [PATCH 1/3] Add test to find stack offset also where there's FRED stack padding --- get_stack_offset.bpf.c | 49 ++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/get_stack_offset.bpf.c b/get_stack_offset.bpf.c index c483892..d36eccf 100644 --- a/get_stack_offset.bpf.c +++ b/get_stack_offset.bpf.c @@ -28,6 +28,41 @@ #include "get_stack_offset.h" +/* ------------------------------------------------------------------ + * Universal helpers for locating task_pt_regs() on kernels both + * with and without FRED stack padding (0 or 16 bytes). + * Works on every x86‑64 kernel from 4.14 → latest. + * ------------------------------------------------------------------ */ + +static const __u8 fred_pads[] = { 0, 16 }; /* legacy & FRED paddings */ + +static __always_inline bool regs_match(struct pt_regs *regs) +{ + __u64 si, dx; + + /* Ignore faults: the pointer may be garbage */ + (void)bpf_probe_read(&si, sizeof(si), ®s->si); + (void)bpf_probe_read(&dx, sizeof(dx), ®s->dx); + + return si == SI_VALUE && dx == DX_VALUE; +} + +/* + * Attempt to interpret @base_ptr as the bottom of a kernel stack and return + * byte‑offset within task_struct if its pt_regs frame matches our sentinel. + * Returns ≥ 0 when found, or ‑1 if not found for any known padding. + */ +static __always_inline int find_regs(const __u64 *base_ptr, __u32 index) +{ +#pragma unroll + for (int p = 0; p < (int)(sizeof(fred_pads) / sizeof(fred_pads[0])); p++) { + struct pt_regs *r = (struct pt_regs *)((unsigned long)base_ptr + + THREAD_SIZE - fred_pads[p]) - 1; + if (regs_match(r)) + return index * sizeof(__u64); + } + return -1; +} #define NUM_TAIL_CALLS 26 #define TOTAL_ITERS (MAX_TASK_STRUCT / sizeof(__u64)) @@ -123,22 +158,14 @@ int do_write(struct pt_regs *ctx) continue; } - // implementing task_pt_regs() for x86_64 here. - struct pt_regs *regs = (struct pt_regs*)((unsigned long)maybe_stack + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING) - 1; - - __u64 pt_regs_si; - __u64 pt_regs_dx; - // ignore errors, "pointer" may not be a pointer at all. - (void)bpf_probe_read(&pt_regs_si, sizeof(pt_regs_si), ®s->si); - (void)bpf_probe_read(&pt_regs_dx, sizeof(pt_regs_dx), ®s->dx); - - if (pt_regs_si== SI_VALUE && pt_regs_dx == DX_VALUE) { + int off = find_regs(maybe_stack, base + i); + if (off >= 0) { if (out.status != STATUS_NOTFOUND) { out.status = STATUS_DUP; goto out; } - out.offset = (base + i) * sizeof(__u64); + out.offset = off; out.status = STATUS_OK; // continue searching, check for dups } From 56a133ec8b873d787cb66afb21e629f7f8ed619d Mon Sep 17 00:00:00 2001 From: Ron Sobol Date: Sun, 30 Mar 2025 13:18:54 +0300 Subject: [PATCH 2/3] Replace canonical kernel pointer checking --- get_stack_offset.bpf.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/get_stack_offset.bpf.c b/get_stack_offset.bpf.c index d36eccf..5b29adc 100644 --- a/get_stack_offset.bpf.c +++ b/get_stack_offset.bpf.c @@ -34,6 +34,9 @@ * Works on every x86‑64 kernel from 4.14 → latest. * ------------------------------------------------------------------ */ +/* Sign‑bit test that is true for any canonical kernel pointer */ +#define IS_KERNEL_PTR(ptr) ((long)(ptr) < 0) + static const __u8 fred_pads[] = { 0, 16 }; /* legacy & FRED paddings */ static __always_inline bool regs_match(struct pt_regs *regs) @@ -68,11 +71,6 @@ static __always_inline int find_regs(const __u64 *base_ptr, __u32 index) #define TOTAL_ITERS (MAX_TASK_STRUCT / sizeof(__u64)) #define ITERS_PER_PROG (TOTAL_ITERS / NUM_TAIL_CALLS) -// this is correct for x86_64 unless 5-level page tables are enabled (in which case, the address -// is lower, but I don't think we'll encounter it any time soon) -// this is 0xffff800000000000, see "Canonical form addresses" in https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details -#define MIN_CANONICAL_KERNEL_ADDRESS (~1UL - ((1UL << 47) - 2)) - // key - zero // value - struct output struct { @@ -152,9 +150,8 @@ int do_write(struct pt_regs *ctx) goto out; } - // make sure it's canonical (and in kernel space), otherwise we might get a WARN_ONCE - // (see ex_handler_uaccess() in the kernel, happens on 5.4). - if ((unsigned long)maybe_stack <= MIN_CANONICAL_KERNEL_ADDRESS) { + /* Quickly discard non‑canonical or user addresses */ + if (!IS_KERNEL_PTR(maybe_stack)) { continue; } From c5526e706c36efea9e2f75ceeb603636cf386ec7 Mon Sep 17 00:00:00 2001 From: Ron Sobol Date: Mon, 21 Apr 2025 23:44:00 +0300 Subject: [PATCH 3/3] Fix missing include --- get_stack_offset.bpf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/get_stack_offset.bpf.c b/get_stack_offset.bpf.c index 5b29adc..cc89f39 100644 --- a/get_stack_offset.bpf.c +++ b/get_stack_offset.bpf.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "get_stack_offset.h"