lkml.org 
[lkml]   [2022]   [Jan]   [31]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v7 09/10] fprobe: Introduce FPROBE_FL_KPROBE_SHARED flag for fprobe
    Date
    Introduce FPROBE_FL_KPROBE_SHARED flag for sharing fprobe callback with
    kprobes safely from the viewpoint of recursion.

    Since the recursion safety of the fprobe (and ftrace) is a bit different
    from the kprobes, this may cause an issue if user wants to run the same
    code from the fprobe and the kprobes.

    The kprobes has per-cpu 'current_kprobe' variable which protects the
    kprobe handler from recursion in any case. On the other hand, the fprobe
    uses only ftrace_test_recursion_trylock(), which will allow interrupt
    context calls another (or same) fprobe during the fprobe user handler is
    running.

    This is not a matter in cases if the common callback shared among the
    kprobes and the fprobe has its own recursion detection, or it can handle
    the recursion in the different contexts (normal/interrupt/NMI.)
    But if it relies on the 'current_kprobe' recursion lock, it has to check
    kprobe_running() and use kprobe_busy_*() APIs.

    Fprobe has FPROBE_FL_KPROBE_SHARED flag to do this. If your common callback
    code will be shared with kprobes, please set FPROBE_FL_KPROBE_SHARED
    *before* registering the fprobe, like;

    fprobe.flags = FPROBE_FL_KPROBE_SHARED;

    register_fprobe(&fprobe, "func*", NULL);

    This will protect your common callback from the nested call.

    Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
    ---
    include/linux/fprobe.h | 12 ++++++++++++
    include/linux/kprobes.h | 3 +++
    kernel/trace/fprobe.c | 19 ++++++++++++++++++-
    3 files changed, 33 insertions(+), 1 deletion(-)

    diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h
    index acfdcc37acf6..94b9386d7267 100644
    --- a/include/linux/fprobe.h
    +++ b/include/linux/fprobe.h
    @@ -26,13 +26,25 @@ struct fprobe {
    void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs);
    };

    +/* This fprobe is soft-disabled. */
    #define FPROBE_FL_DISABLED 1

    +/*
    + * This fprobe handler will be shared with kprobes.
    + * This flag must be set before registering.
    + */
    +#define FPROBE_FL_KPROBE_SHARED 2
    +
    static inline bool fprobe_disabled(struct fprobe *fp)
    {
    return (fp) ? fp->flags & FPROBE_FL_DISABLED : false;
    }

    +static inline bool fprobe_shared_with_kprobes(struct fprobe *fp)
    +{
    + return (fp) ? fp->flags & FPROBE_FL_KPROBE_SHARED : false;
    +}
    +
    #ifdef CONFIG_FPROBE
    int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter);
    int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num);
    diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
    index 8c8f7a4d93af..efe4fc364f6a 100644
    --- a/include/linux/kprobes.h
    +++ b/include/linux/kprobes.h
    @@ -433,6 +433,9 @@ static inline struct kprobe *kprobe_running(void)
    {
    return NULL;
    }
    +#define kprobe_busy_begin() do {} while (0)
    +#define kprobe_busy_end() do {} while (0)
    +
    static inline int register_kprobe(struct kprobe *p)
    {
    return -EOPNOTSUPP;
    diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
    index d733c0d9cb09..4881b98f35ab 100644
    --- a/kernel/trace/fprobe.c
    +++ b/kernel/trace/fprobe.c
    @@ -56,6 +56,20 @@ static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
    }
    NOKPROBE_SYMBOL(fprobe_handler);

    +static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,
    + struct ftrace_ops *ops, struct ftrace_regs *fregs)
    +{
    + struct fprobe *fp = container_of(ops, struct fprobe, ops);
    +
    + if (unlikely(kprobe_running())) {
    + fp->nmissed++;
    + return;
    + }
    + kprobe_busy_begin();
    + fprobe_handler(ip, parent_ip, ops, fregs);
    + kprobe_busy_end();
    +}
    +
    static void fprobe_exit_handler(struct rethook_node *rh, void *data,
    struct pt_regs *regs)
    {
    @@ -109,7 +123,10 @@ static unsigned long *get_ftrace_locations(const char **syms, int num)
    static void fprobe_init(struct fprobe *fp)
    {
    fp->nmissed = 0;
    - fp->ops.func = fprobe_handler;
    + if (fprobe_shared_with_kprobes(fp))
    + fp->ops.func = fprobe_kprobe_handler;
    + else
    + fp->ops.func = fprobe_handler;
    fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS;
    }

    \
     
     \ /
      Last update: 2022-01-31 06:04    [W:4.158 / U:0.220 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site