// SPDX-License-Identifier: GPL-2.0
/*
* Fprobe-based tracing events
* Copyright (C) 2022 Google LLC.
*/
#define pr_fmt(fmt) "trace_fprobe: " fmt
#include <linux/fprobe.h>
#include <linux/module.h>
#include <linux/rculist.h>
#include <linux/security.h>
#include <linux/tracepoint.h>
#include <linux/uaccess.h>
#include "trace_dynevent.h"
#include "trace_probe.h"
#include "trace_probe_kernel.h"
#include "trace_probe_tmpl.h"
#define FPROBE_EVENT_SYSTEM "fprobes"
#define TRACEPOINT_EVENT_SYSTEM "tracepoints"
#define RETHOOK_MAXACTIVE_MAX 4096
static int trace_fprobe_create(const char *raw_command);
static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev);
static int trace_fprobe_release(struct dyn_event *ev);
static bool trace_fprobe_is_busy(struct dyn_event *ev);
static bool trace_fprobe_match(const char *system, const char *event,
int argc, const char **argv, struct dyn_event *ev);
static struct dyn_event_operations trace_fprobe_ops = {
.create = trace_fprobe_create,
.show = trace_fprobe_show,
.is_busy = trace_fprobe_is_busy,
.free = trace_fprobe_release,
.match = trace_fprobe_match,
};
/*
* Fprobe event core functions
*/
struct trace_fprobe {
struct dyn_event devent;
struct fprobe fp;
const char *symbol;
struct tracepoint *tpoint;
struct module *mod;
struct trace_probe tp;
};
static bool is_trace_fprobe(struct dyn_event *ev)
{
return ev->ops == &trace_fprobe_ops;
}
static struct trace_fprobe *to_trace_fprobe(struct dyn_event *ev)
{
return container_of(ev, struct trace_fprobe, devent);
}
/**
* for_each_trace_fprobe - iterate over the trace_fprobe list
* @pos: the struct trace_fprobe * for each entry
* @dpos: the struct dyn_event * to use as a loop cursor
*/
#define for_each_trace_fprobe(pos, dpos) \
for_each_dyn_event(dpos) \
if (is_trace_fprobe(dpos) && (pos = to_trace_fprobe(dpos)))
static bool trace_fprobe_is_return(struct trace_fprobe *tf)
{
return tf->fp.exit_handler != NULL;
}
static bool trace_fprobe_is_tracepoint(struct trace_fprobe *tf)
{
return tf->tpoint != NULL;
}
static const char *trace_fprobe_symbol(struct trace_fprobe *tf)
{
return tf->symbol ? tf->symbol : "unknown";
}
static bool trace_fprobe_is_busy(struct dyn_event *ev)
{
struct trace_fprobe *tf = to_trace_fprobe(ev);
return trace_probe_is_enabled(&tf->tp);
}
static bool trace_fprobe_match_command_head(struct trace_fprobe *tf,
int argc, const char **argv)
{
char buf[MAX_ARGSTR_LEN + 1];
if (!argc)
return true;
snprintf(buf, sizeof(buf), "%s", trace_fprobe_symbol(tf));
if (strcmp(buf, argv[0]))
return false;
argc--; argv++;
return trace_probe_match_command_args(&tf->tp, argc, argv);
}
static bool trace_fprobe_match(const char *system, const char *event,
int argc, const char **argv, struct dyn_event *ev)
{
struct trace_fprobe *tf = to_trace_fprobe(ev);
if (event[0] != '\0' && strcmp(trace_probe_name(&tf->tp), event))
return false;
if (system && strcmp(trace_probe_group_name(&tf->tp), system))
return false;
return <