]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tracing/fprobe: Reject registration of a registered fprobe before init
authorMasami Hiramatsu (Google) <mhiramat@kernel.org>
Mon, 20 Apr 2026 14:00:48 +0000 (23:00 +0900)
committerMasami Hiramatsu (Google) <mhiramat@kernel.org>
Tue, 21 Apr 2026 14:59:29 +0000 (23:59 +0900)
Reject registration of a registered fprobe which is on the fprobe
hash table before initializing fprobe.
The add_fprobe_hash() checks this re-register fprobe, but since
fprobe_init() clears hlist_array field, it is too late to check it.
It has to check the re-registration before touncing fprobe.

Link: https://lore.kernel.org/all/177669364845.132053.18375367916162315835.stgit@mhiramat.tok.corp.google.com/
Fixes: 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
kernel/trace/fprobe.c

index 56d1450179027241cfe4142b72d6910d0a80f170..af9ba725087469456f256ff5eb442220ba68af77 100644 (file)
@@ -4,6 +4,7 @@
  */
 #define pr_fmt(fmt) "fprobe: " fmt
 
+#include <linux/cleanup.h>
 #include <linux/err.h>
 #include <linux/fprobe.h>
 #include <linux/kallsyms.h>
@@ -107,7 +108,7 @@ static bool delete_fprobe_node(struct fprobe_hlist_node *node)
 }
 
 /* Check existence of the fprobe */
-static bool is_fprobe_still_exist(struct fprobe *fp)
+static bool fprobe_registered(struct fprobe *fp)
 {
        struct hlist_head *head;
        struct fprobe_hlist *fph;
@@ -120,7 +121,7 @@ static bool is_fprobe_still_exist(struct fprobe *fp)
        }
        return false;
 }
-NOKPROBE_SYMBOL(is_fprobe_still_exist);
+NOKPROBE_SYMBOL(fprobe_registered);
 
 static int add_fprobe_hash(struct fprobe *fp)
 {
@@ -132,9 +133,6 @@ static int add_fprobe_hash(struct fprobe *fp)
        if (WARN_ON_ONCE(!fph))
                return -EINVAL;
 
-       if (is_fprobe_still_exist(fp))
-               return -EEXIST;
-
        head = &fprobe_table[hash_ptr(fp, FPROBE_HASH_BITS)];
        hlist_add_head_rcu(&fp->hlist_array->hlist, head);
        return 0;
@@ -149,7 +147,7 @@ static int del_fprobe_hash(struct fprobe *fp)
        if (WARN_ON_ONCE(!fph))
                return -EINVAL;
 
-       if (!is_fprobe_still_exist(fp))
+       if (!fprobe_registered(fp))
                return -ENOENT;
 
        fph->fp = NULL;
@@ -480,7 +478,7 @@ static void fprobe_return(struct ftrace_graph_ret *trace,
                if (!fp)
                        break;
                curr += FPROBE_HEADER_SIZE_IN_LONG;
-               if (is_fprobe_still_exist(fp) && !fprobe_disabled(fp)) {
+               if (fprobe_registered(fp) && !fprobe_disabled(fp)) {
                        if (WARN_ON_ONCE(curr + size > size_words))
                                break;
                        fp->exit_handler(fp, trace->func, ret_ip, fregs,
@@ -839,12 +837,14 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
        struct fprobe_hlist *hlist_array;
        int ret, i;
 
+       guard(mutex)(&fprobe_mutex);
+       if (fprobe_registered(fp))
+               return -EEXIST;
+
        ret = fprobe_init(fp, addrs, num);
        if (ret)
                return ret;
 
-       mutex_lock(&fprobe_mutex);
-
        hlist_array = fp->hlist_array;
        if (fprobe_is_ftrace(fp))
                ret = fprobe_ftrace_add_ips(addrs, num);
@@ -864,7 +864,6 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
                                delete_fprobe_node(&hlist_array->array[i]);
                }
        }
-       mutex_unlock(&fprobe_mutex);
 
        if (ret)
                fprobe_fail_cleanup(fp);
@@ -926,7 +925,7 @@ int unregister_fprobe(struct fprobe *fp)
        int ret = 0, i, count;
 
        mutex_lock(&fprobe_mutex);
-       if (!fp || !is_fprobe_still_exist(fp)) {
+       if (!fp || !fprobe_registered(fp)) {
                ret = -EINVAL;
                goto out;
        }