Merge tag 'probes-fixes-v7.1-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull probes fixes from Masami Hiramatsu:

 - kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()

   Since the ftrace adds its NOPs at .kprobes.text section (which stores
   an array), a wrong entry is added when loading a module which uses
   "__kprobes" attribute.

   To solve this, add "notrace" to __kprobes functions

 - test_kprobes: clear kprobes between test runs

   Clear all kprobes in the test program after running a test set,
   because Kunit test can run several times

 - fprobe: Fix unregister_fprobe() to wait for RCU grace period

   Since the fprobe data structure is removed with hlist_del_rcu(), it
   should wait for the RCU grace period. If the caller waits for RCU, we
   can use the async variant (e.g. eBPF)

* tag 'probes-fixes-v7.1-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  fprobe: Fix unregister_fprobe() to wait for RCU grace period
  test_kprobes: clear kprobes between test runs
  kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
This commit is contained in:
Linus Torvalds
2026-05-12 10:18:02 -07:00
5 changed files with 47 additions and 15 deletions

View File

@@ -14,7 +14,7 @@ static unsigned long __used \
_kbl_addr_##fname = (unsigned long)fname;
# define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname)
/* Use this to forbid a kprobes attach on very low level functions */
# define __kprobes __section(".kprobes.text")
# define __kprobes notrace __section(".kprobes.text")
# define nokprobe_inline __always_inline
#else
# define NOKPROBE_SYMBOL(fname)

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -12,6 +12,12 @@
#define div_factor 3
#define KP_CLEAR(_kp) \
do { \
(_kp).addr = NULL; \
(_kp).flags = 0; \
} while (0)
static u32 rand1, preh_val, posth_val;
static u32 (*target)(u32 value);
static u32 (*recursed_target)(u32 value);
@@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test)
current_test = test;
/* addr and flags should be cleard for reusing kprobe. */
kp.addr = NULL;
kp.flags = 0;
KUNIT_EXPECT_EQ(test, 0, register_kprobes(kps, 2));
preh_val = 0;
posth_val = 0;
@@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test)
struct kretprobe *rps[2] = {&rp, &rp2};
current_test = test;
/* addr and flags should be cleard for reusing kprobe. */
rp.kp.addr = NULL;
rp.kp.flags = 0;
KUNIT_EXPECT_EQ(test, 0, register_kretprobes(rps, 2));
krph_val = 0;
@@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test)
unsigned long myretaddr = (unsigned long)__builtin_return_address(0);
current_test = test;
rp3.kp.addr = NULL;
rp3.kp.flags = 0;
/*
* Run the stacktrace_driver() to record correct return address in
@@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test)
struct kretprobe *rps[2] = {&rp3, &rp4};
current_test = test;
rp3.kp.addr = NULL;
rp3.kp.flags = 0;
//KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver());
@@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test)
static int kprobes_test_init(struct kunit *test)
{
KP_CLEAR(kp);
KP_CLEAR(kp2);
KP_CLEAR(kp_missed);
#ifdef CONFIG_KRETPROBES
KP_CLEAR(rp.kp);
KP_CLEAR(rp2.kp);
#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE
KP_CLEAR(rp3.kp);
KP_CLEAR(rp4.kp);
#endif
#endif
target = kprobe_target;
target2 = kprobe_target2;
recursed_target = kprobe_recursed_target;