mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 04:21:09 -04:00
fprobe: Fix unregister_fprobe() to wait for RCU grace period
Commit4346ba1604("fprobe: Rewrite fprobe on function-graph tracer") changed fprobe to register struct fprobe to an rcu-hlist, but it forgot to wait for RCU GP. Thus there can be use-after-free if the fprobe is released right after unregistering. This can be happened on fprobe event and sample module code. To fix this issue, add synchronize_rcu() in unregister_fprobe(). Note that BPF is OK because fprobe is used as a part of bpf_kprobe_multi_link. This unregisters its fprobe in bpf_kprobe_multi_link_release() and it is deallocated via bpf_kprobe_multi_link_dealloc(), which is invoked from bpf_link_defer_dealloc_rcu_gp() RCU callback. For BPF, this also introduced unregister_fprobe_async() which does NOT wait for RCU grace priod. Link: https://lore.kernel.org/all/177813998919.256460.2809243930741138224.stgit@mhiramat.tok.corp.google.com/ Fixes:4346ba1604("fprobe: Rewrite fprobe on function-graph tracer") Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
This commit is contained in:
@@ -94,6 +94,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter
|
||||
int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num);
|
||||
int register_fprobe_syms(struct fprobe *fp, const char **syms, int num);
|
||||
int unregister_fprobe(struct fprobe *fp);
|
||||
int unregister_fprobe_async(struct fprobe *fp);
|
||||
bool fprobe_is_registered(struct fprobe *fp);
|
||||
int fprobe_count_ips_from_filter(const char *filter, const char *notfilter);
|
||||
#else
|
||||
@@ -113,6 +114,10 @@ static inline int unregister_fprobe(struct fprobe *fp)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int unregister_fprobe_async(struct fprobe *fp)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline bool fprobe_is_registered(struct fprobe *fp)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -2384,7 +2384,8 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link)
|
||||
struct bpf_kprobe_multi_link *kmulti_link;
|
||||
|
||||
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
|
||||
unregister_fprobe(&kmulti_link->fp);
|
||||
/* Don't wait for RCU GP here. */
|
||||
unregister_fprobe_async(&kmulti_link->fp);
|
||||
kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt);
|
||||
}
|
||||
|
||||
|
||||
@@ -1093,14 +1093,15 @@ static int unregister_fprobe_nolock(struct fprobe *fp)
|
||||
}
|
||||
|
||||
/**
|
||||
* unregister_fprobe() - Unregister fprobe.
|
||||
* unregister_fprobe_async() - Unregister fprobe without RCU GP wait
|
||||
* @fp: A fprobe data structure to be unregistered.
|
||||
*
|
||||
* Unregister fprobe (and remove ftrace hooks from the function entries).
|
||||
* This function will NOT wait until the fprobe is no longer used.
|
||||
*
|
||||
* Return 0 if @fp is unregistered successfully, -errno if not.
|
||||
*/
|
||||
int unregister_fprobe(struct fprobe *fp)
|
||||
int unregister_fprobe_async(struct fprobe *fp)
|
||||
{
|
||||
guard(mutex)(&fprobe_mutex);
|
||||
if (!fp || !fprobe_registered(fp))
|
||||
@@ -1108,6 +1109,24 @@ int unregister_fprobe(struct fprobe *fp)
|
||||
|
||||
return unregister_fprobe_nolock(fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* unregister_fprobe() - Unregister fprobe with RCU GP wait
|
||||
* @fp: A fprobe data structure to be unregistered.
|
||||
*
|
||||
* Unregister fprobe (and remove ftrace hooks from the function entries).
|
||||
* This function will block until the fprobe is no longer used.
|
||||
*
|
||||
* Return 0 if @fp is unregistered successfully, -errno if not.
|
||||
*/
|
||||
int unregister_fprobe(struct fprobe *fp)
|
||||
{
|
||||
int ret = unregister_fprobe_async(fp);
|
||||
|
||||
if (!ret)
|
||||
synchronize_rcu();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_fprobe);
|
||||
|
||||
static int __init fprobe_initcall(void)
|
||||
|
||||
Reference in New Issue
Block a user