Merge tag 'probes-fixes-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull probe fix from Masami Hiramatsu: - Fix race condition in kprobe initialization causing NULL pointer dereference. This happens on weak memory model, which does not correctly manage the flags access with appropriate memory barriers. Use RELEASE-ACQUIRE to fix it. * tag 'probes-fixes-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: tracing: Fix race condition in kprobe initialization causing NULL pointer dereference
This commit is contained in:
@@ -522,13 +522,14 @@ static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip,
|
||||
void *entry_data)
|
||||
{
|
||||
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
|
||||
unsigned int flags = trace_probe_load_flag(&tf->tp);
|
||||
int ret = 0;
|
||||
|
||||
if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
|
||||
if (flags & TP_FLAG_TRACE)
|
||||
fentry_trace_func(tf, entry_ip, fregs);
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
|
||||
if (flags & TP_FLAG_PROFILE)
|
||||
ret = fentry_perf_func(tf, entry_ip, fregs);
|
||||
#endif
|
||||
return ret;
|
||||
@@ -540,11 +541,12 @@ static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip,
|
||||
void *entry_data)
|
||||
{
|
||||
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
|
||||
unsigned int flags = trace_probe_load_flag(&tf->tp);
|
||||
|
||||
if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
|
||||
if (flags & TP_FLAG_TRACE)
|
||||
fexit_trace_func(tf, entry_ip, ret_ip, fregs, entry_data);
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
|
||||
if (flags & TP_FLAG_PROFILE)
|
||||
fexit_perf_func(tf, entry_ip, ret_ip, fregs, entry_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1815,14 +1815,15 @@ static int kprobe_register(struct trace_event_call *event,
|
||||
static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
|
||||
{
|
||||
struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp);
|
||||
unsigned int flags = trace_probe_load_flag(&tk->tp);
|
||||
int ret = 0;
|
||||
|
||||
raw_cpu_inc(*tk->nhit);
|
||||
|
||||
if (trace_probe_test_flag(&tk->tp, TP_FLAG_TRACE))
|
||||
if (flags & TP_FLAG_TRACE)
|
||||
kprobe_trace_func(tk, regs);
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
if (trace_probe_test_flag(&tk->tp, TP_FLAG_PROFILE))
|
||||
if (flags & TP_FLAG_PROFILE)
|
||||
ret = kprobe_perf_func(tk, regs);
|
||||
#endif
|
||||
return ret;
|
||||
@@ -1834,6 +1835,7 @@ kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
struct kretprobe *rp = get_kretprobe(ri);
|
||||
struct trace_kprobe *tk;
|
||||
unsigned int flags;
|
||||
|
||||
/*
|
||||
* There is a small chance that get_kretprobe(ri) returns NULL when
|
||||
@@ -1846,10 +1848,11 @@ kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
tk = container_of(rp, struct trace_kprobe, rp);
|
||||
raw_cpu_inc(*tk->nhit);
|
||||
|
||||
if (trace_probe_test_flag(&tk->tp, TP_FLAG_TRACE))
|
||||
flags = trace_probe_load_flag(&tk->tp);
|
||||
if (flags & TP_FLAG_TRACE)
|
||||
kretprobe_trace_func(tk, ri, regs);
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
if (trace_probe_test_flag(&tk->tp, TP_FLAG_PROFILE))
|
||||
if (flags & TP_FLAG_PROFILE)
|
||||
kretprobe_perf_func(tk, ri, regs);
|
||||
#endif
|
||||
return 0; /* We don't tweak kernel, so just return 0 */
|
||||
|
||||
@@ -271,16 +271,21 @@ struct event_file_link {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static inline unsigned int trace_probe_load_flag(struct trace_probe *tp)
|
||||
{
|
||||
return smp_load_acquire(&tp->event->flags);
|
||||
}
|
||||
|
||||
static inline bool trace_probe_test_flag(struct trace_probe *tp,
|
||||
unsigned int flag)
|
||||
{
|
||||
return !!(tp->event->flags & flag);
|
||||
return !!(trace_probe_load_flag(tp) & flag);
|
||||
}
|
||||
|
||||
static inline void trace_probe_set_flag(struct trace_probe *tp,
|
||||
unsigned int flag)
|
||||
{
|
||||
tp->event->flags |= flag;
|
||||
smp_store_release(&tp->event->flags, tp->event->flags | flag);
|
||||
}
|
||||
|
||||
static inline void trace_probe_clear_flag(struct trace_probe *tp,
|
||||
|
||||
@@ -1547,6 +1547,7 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs,
|
||||
struct trace_uprobe *tu;
|
||||
struct uprobe_dispatch_data udd;
|
||||
struct uprobe_cpu_buffer *ucb = NULL;
|
||||
unsigned int flags;
|
||||
int ret = 0;
|
||||
|
||||
tu = container_of(con, struct trace_uprobe, consumer);
|
||||
@@ -1561,11 +1562,12 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs,
|
||||
if (WARN_ON_ONCE(!uprobe_cpu_buffer))
|
||||
return 0;
|
||||
|
||||
if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
|
||||
flags = trace_probe_load_flag(&tu->tp);
|
||||
if (flags & TP_FLAG_TRACE)
|
||||
ret |= uprobe_trace_func(tu, regs, &ucb);
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
if (trace_probe_test_flag(&tu->tp, TP_FLAG_PROFILE))
|
||||
if (flags & TP_FLAG_PROFILE)
|
||||
ret |= uprobe_perf_func(tu, regs, &ucb);
|
||||
#endif
|
||||
uprobe_buffer_put(ucb);
|
||||
@@ -1579,6 +1581,7 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
|
||||
struct trace_uprobe *tu;
|
||||
struct uprobe_dispatch_data udd;
|
||||
struct uprobe_cpu_buffer *ucb = NULL;
|
||||
unsigned int flags;
|
||||
|
||||
tu = container_of(con, struct trace_uprobe, consumer);
|
||||
|
||||
@@ -1590,11 +1593,12 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
|
||||
if (WARN_ON_ONCE(!uprobe_cpu_buffer))
|
||||
return 0;
|
||||
|
||||
if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
|
||||
flags = trace_probe_load_flag(&tu->tp);
|
||||
if (flags & TP_FLAG_TRACE)
|
||||
uretprobe_trace_func(tu, func, regs, &ucb);
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
if (trace_probe_test_flag(&tu->tp, TP_FLAG_PROFILE))
|
||||
if (flags & TP_FLAG_PROFILE)
|
||||
uretprobe_perf_func(tu, func, regs, &ucb);
|
||||
#endif
|
||||
uprobe_buffer_put(ucb);
|
||||
|
||||
Reference in New Issue
Block a user