Commit 54ecbe6f authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Alexei Starovoitov
Browse files

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: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
Tested-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/164735285066.1084943.9259661137330166643.stgit@devnote2
parent cad9931f
Loading
Loading
Loading
Loading
+100 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Return hooking with list-based shadow stack.
 */
#ifndef _LINUX_RETHOOK_H
#define _LINUX_RETHOOK_H

#include <linux/compiler.h>
#include <linux/freelist.h>
#include <linux/kallsyms.h>
#include <linux/llist.h>
#include <linux/rcupdate.h>
#include <linux/refcount.h>

struct rethook_node;

typedef void (*rethook_handler_t) (struct rethook_node *, void *, struct pt_regs *);

/**
 * struct rethook - The rethook management data structure.
 * @data: The user-defined data storage.
 * @handler: The user-defined return hook handler.
 * @pool: The pool of struct rethook_node.
 * @ref: The reference counter.
 * @rcu: The rcu_head for deferred freeing.
 *
 * Don't embed to another data structure, because this is a self-destructive
 * data structure when all rethook_node are freed.
 */
struct rethook {
	void			*data;
	rethook_handler_t	handler;
	struct freelist_head	pool;
	refcount_t		ref;
	struct rcu_head		rcu;
};

/**
 * struct rethook_node - The rethook shadow-stack entry node.
 * @freelist: The freelist, linked to struct rethook::pool.
 * @rcu: The rcu_head for deferred freeing.
 * @llist: The llist, linked to a struct task_struct::rethooks.
 * @rethook: The pointer to the struct rethook.
 * @ret_addr: The storage for the real return address.
 * @frame: The storage for the frame pointer.
 *
 * You can embed this to your extended data structure to store any data
 * on each entry of the shadow stack.
 */
struct rethook_node {
	union {
		struct freelist_node freelist;
		struct rcu_head      rcu;
	};
	struct llist_node	llist;
	struct rethook		*rethook;
	unsigned long		ret_addr;
	unsigned long		frame;
};

struct rethook *rethook_alloc(void *data, rethook_handler_t handler);
void rethook_free(struct rethook *rh);
void rethook_add_node(struct rethook *rh, struct rethook_node *node);
struct rethook_node *rethook_try_get(struct rethook *rh);
void rethook_recycle(struct rethook_node *node);
void rethook_hook(struct rethook_node *node, struct pt_regs *regs, bool mcount);
unsigned long rethook_find_ret_addr(struct task_struct *tsk, unsigned long frame,
				    struct llist_node **cur);

/* Arch dependent code must implement arch_* and trampoline code */
void arch_rethook_prepare(struct rethook_node *node, struct pt_regs *regs, bool mcount);
void arch_rethook_trampoline(void);

/**
 * is_rethook_trampoline() - Check whether the address is rethook trampoline
 * @addr: The address to be checked
 *
 * Return true if the @addr is the rethook trampoline address.
 */
static inline bool is_rethook_trampoline(unsigned long addr)
{
	return addr == (unsigned long)dereference_symbol_descriptor(arch_rethook_trampoline);
}

/* If the architecture needs to fixup the return address, implement it. */
void arch_rethook_fixup_return(struct pt_regs *regs,
			       unsigned long correct_ret_addr);

/* Generic trampoline handler, arch code must prepare asm stub */
unsigned long rethook_trampoline_handler(struct pt_regs *regs,
					 unsigned long frame);

#ifdef CONFIG_RETHOOK
void rethook_flush_task(struct task_struct *tk);
#else
#define rethook_flush_task(tsk)	do { } while (0)
#endif

#endif
+3 −0
Original line number Diff line number Diff line
@@ -1481,6 +1481,9 @@ struct task_struct {
#ifdef CONFIG_KRETPROBES
	struct llist_head               kretprobe_instances;
#endif
#ifdef CONFIG_RETHOOK
	struct llist_head               rethooks;
#endif

#ifdef CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH
	/*
+2 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@
#include <linux/compat.h>
#include <linux/io_uring.h>
#include <linux/kprobes.h>
#include <linux/rethook.h>

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

	kprobe_flush_task(tsk);
	rethook_flush_task(tsk);
	perf_event_delayed_put(tsk);
	trace_sched_process_free(tsk);
	put_task_struct(tsk);
+3 −0
Original line number Diff line number Diff line
@@ -2255,6 +2255,9 @@ static __latent_entropy struct task_struct *copy_process(
#ifdef CONFIG_KRETPROBES
	p->kretprobe_instances.first = NULL;
#endif
#ifdef CONFIG_RETHOOK
	p->rethooks.first = NULL;
#endif

	/*
	 * Ensure that the cgroup subsystem policies allow the new process to be
+11 −0
Original line number Diff line number Diff line
@@ -10,6 +10,17 @@ config USER_STACKTRACE_SUPPORT
config NOP_TRACER
	bool

config HAVE_RETHOOK
	bool

config RETHOOK
	bool
	depends on HAVE_RETHOOK
	help
	  Enable generic return hooking feature. This is an internal
	  API, which will be used by other function-entry hooking
	  features like fprobe and kprobes.

config HAVE_FUNCTION_TRACER
	bool
	help
Loading