]> git.itanic.dy.fi Git - linux-stable/commitdiff
fprobe: add recursion detection in fprobe_exit_handler
authorZe Gao <zegao2021@gmail.com>
Wed, 17 May 2023 03:45:08 +0000 (11:45 +0800)
committerMasami Hiramatsu (Google) <mhiramat@kernel.org>
Wed, 17 May 2023 22:08:01 +0000 (07:08 +0900)
fprobe_hander and fprobe_kprobe_handler has guarded ftrace recursion
detection but fprobe_exit_handler has not, which possibly introduce
recursive calls if the fprobe exit callback calls any traceable
functions. Checking in fprobe_hander or fprobe_kprobe_handler
is not enough and misses this case.

So add recursion free guard the same way as fprobe_hander. Since
ftrace recursion check does not employ ip(s), so here use entry_ip and
entry_parent_ip the same as fprobe_handler.

Link: https://lore.kernel.org/all/20230517034510.15639-4-zegao@tencent.com/
Fixes: 5b0ab78998e3 ("fprobe: Add exit_handler support")
Signed-off-by: Ze Gao <zegao@tencent.com>
Cc: stable@vger.kernel.org
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
kernel/trace/fprobe.c

index 7a692c02f787f705cc8e49a3c10a0eb78bc1a709..18d36842faf575d2057439aa22f5b8b4064858af 100644 (file)
@@ -17,6 +17,7 @@
 struct fprobe_rethook_node {
        struct rethook_node node;
        unsigned long entry_ip;
+       unsigned long entry_parent_ip;
        char data[];
 };
 
@@ -39,6 +40,7 @@ static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip,
                }
                fpr = container_of(rh, struct fprobe_rethook_node, node);
                fpr->entry_ip = ip;
+               fpr->entry_parent_ip = parent_ip;
                if (fp->entry_data_size)
                        entry_data = fpr->data;
        }
@@ -114,14 +116,26 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,
 {
        struct fprobe *fp = (struct fprobe *)data;
        struct fprobe_rethook_node *fpr;
+       int bit;
 
        if (!fp || fprobe_disabled(fp))
                return;
 
        fpr = container_of(rh, struct fprobe_rethook_node, node);
 
+       /*
+        * we need to assure no calls to traceable functions in-between the
+        * end of fprobe_handler and the beginning of fprobe_exit_handler.
+        */
+       bit = ftrace_test_recursion_trylock(fpr->entry_ip, fpr->entry_parent_ip);
+       if (bit < 0) {
+               fp->nmissed++;
+               return;
+       }
+
        fp->exit_handler(fp, fpr->entry_ip, regs,
                         fp->entry_data_size ? (void *)fpr->data : NULL);
+       ftrace_test_recursion_unlock(bit);
 }
 NOKPROBE_SYMBOL(fprobe_exit_handler);