Skip to content

Commit 54ecbe6

Browse files
mhiramatAlexei Starovoitov
authored andcommitted
rethook: Add a generic return hook
Add a return hook framework which hooks the function return. Most of the logic came from the kretprobe, but this is independent from kretprobe. Note that this is expected to be used with other function entry hooking feature, like ftrace, fprobe, adn kprobes. Eventually this will replace the kretprobe (e.g. kprobe + rethook = kretprobe), but at this moment, this is just an additional hook. Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]> Tested-by: Steven Rostedt (Google) <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/164735285066.1084943.9259661137330166643.stgit@devnote2
1 parent cad9931 commit 54ecbe6

File tree

7 files changed

+437
-0
lines changed

7 files changed

+437
-0
lines changed

include/linux/rethook.h

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Return hooking with list-based shadow stack.
4+
*/
5+
#ifndef _LINUX_RETHOOK_H
6+
#define _LINUX_RETHOOK_H
7+
8+
#include <linux/compiler.h>
9+
#include <linux/freelist.h>
10+
#include <linux/kallsyms.h>
11+
#include <linux/llist.h>
12+
#include <linux/rcupdate.h>
13+
#include <linux/refcount.h>
14+
15+
struct rethook_node;
16+
17+
typedef void (*rethook_handler_t) (struct rethook_node *, void *, struct pt_regs *);
18+
19+
/**
20+
* struct rethook - The rethook management data structure.
21+
* @data: The user-defined data storage.
22+
* @handler: The user-defined return hook handler.
23+
* @pool: The pool of struct rethook_node.
24+
* @ref: The reference counter.
25+
* @rcu: The rcu_head for deferred freeing.
26+
*
27+
* Don't embed to another data structure, because this is a self-destructive
28+
* data structure when all rethook_node are freed.
29+
*/
30+
struct rethook {
31+
void *data;
32+
rethook_handler_t handler;
33+
struct freelist_head pool;
34+
refcount_t ref;
35+
struct rcu_head rcu;
36+
};
37+
38+
/**
39+
* struct rethook_node - The rethook shadow-stack entry node.
40+
* @freelist: The freelist, linked to struct rethook::pool.
41+
* @rcu: The rcu_head for deferred freeing.
42+
* @llist: The llist, linked to a struct task_struct::rethooks.
43+
* @rethook: The pointer to the struct rethook.
44+
* @ret_addr: The storage for the real return address.
45+
* @frame: The storage for the frame pointer.
46+
*
47+
* You can embed this to your extended data structure to store any data
48+
* on each entry of the shadow stack.
49+
*/
50+
struct rethook_node {
51+
union {
52+
struct freelist_node freelist;
53+
struct rcu_head rcu;
54+
};
55+
struct llist_node llist;
56+
struct rethook *rethook;
57+
unsigned long ret_addr;
58+
unsigned long frame;
59+
};
60+
61+
struct rethook *rethook_alloc(void *data, rethook_handler_t handler);
62+
void rethook_free(struct rethook *rh);
63+
void rethook_add_node(struct rethook *rh, struct rethook_node *node);
64+
struct rethook_node *rethook_try_get(struct rethook *rh);
65+
void rethook_recycle(struct rethook_node *node);
66+
void rethook_hook(struct rethook_node *node, struct pt_regs *regs, bool mcount);
67+
unsigned long rethook_find_ret_addr(struct task_struct *tsk, unsigned long frame,
68+
struct llist_node **cur);
69+
70+
/* Arch dependent code must implement arch_* and trampoline code */
71+
void arch_rethook_prepare(struct rethook_node *node, struct pt_regs *regs, bool mcount);
72+
void arch_rethook_trampoline(void);
73+
74+
/**
75+
* is_rethook_trampoline() - Check whether the address is rethook trampoline
76+
* @addr: The address to be checked
77+
*
78+
* Return true if the @addr is the rethook trampoline address.
79+
*/
80+
static inline bool is_rethook_trampoline(unsigned long addr)
81+
{
82+
return addr == (unsigned long)dereference_symbol_descriptor(arch_rethook_trampoline);
83+
}
84+
85+
/* If the architecture needs to fixup the return address, implement it. */
86+
void arch_rethook_fixup_return(struct pt_regs *regs,
87+
unsigned long correct_ret_addr);
88+
89+
/* Generic trampoline handler, arch code must prepare asm stub */
90+
unsigned long rethook_trampoline_handler(struct pt_regs *regs,
91+
unsigned long frame);
92+
93+
#ifdef CONFIG_RETHOOK
94+
void rethook_flush_task(struct task_struct *tk);
95+
#else
96+
#define rethook_flush_task(tsk) do { } while (0)
97+
#endif
98+
99+
#endif
100+

include/linux/sched.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,9 @@ struct task_struct {
14811481
#ifdef CONFIG_KRETPROBES
14821482
struct llist_head kretprobe_instances;
14831483
#endif
1484+
#ifdef CONFIG_RETHOOK
1485+
struct llist_head rethooks;
1486+
#endif
14841487

14851488
#ifdef CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH
14861489
/*

kernel/exit.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include <linux/compat.h>
6565
#include <linux/io_uring.h>
6666
#include <linux/kprobes.h>
67+
#include <linux/rethook.h>
6768

6869
#include <linux/uaccess.h>
6970
#include <asm/unistd.h>
@@ -169,6 +170,7 @@ static void delayed_put_task_struct(struct rcu_head *rhp)
169170
struct task_struct *tsk = container_of(rhp, struct task_struct, rcu);
170171

171172
kprobe_flush_task(tsk);
173+
rethook_flush_task(tsk);
172174
perf_event_delayed_put(tsk);
173175
trace_sched_process_free(tsk);
174176
put_task_struct(tsk);

kernel/fork.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,6 +2255,9 @@ static __latent_entropy struct task_struct *copy_process(
22552255
#ifdef CONFIG_KRETPROBES
22562256
p->kretprobe_instances.first = NULL;
22572257
#endif
2258+
#ifdef CONFIG_RETHOOK
2259+
p->rethooks.first = NULL;
2260+
#endif
22582261

22592262
/*
22602263
* Ensure that the cgroup subsystem policies allow the new process to be

kernel/trace/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ config USER_STACKTRACE_SUPPORT
1010
config NOP_TRACER
1111
bool
1212

13+
config HAVE_RETHOOK
14+
bool
15+
16+
config RETHOOK
17+
bool
18+
depends on HAVE_RETHOOK
19+
help
20+
Enable generic return hooking feature. This is an internal
21+
API, which will be used by other function-entry hooking
22+
features like fprobe and kprobes.
23+
1324
config HAVE_FUNCTION_TRACER
1425
bool
1526
help

kernel/trace/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o
9898
obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o
9999
obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o
100100
obj-$(CONFIG_FPROBE) += fprobe.o
101+
obj-$(CONFIG_RETHOOK) += rethook.o
101102

102103
obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
103104

0 commit comments

Comments
 (0)