Commit 2cd14dff authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 6093a688 9cf9aa7b
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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
}
+7 −4
Original line number Diff line number Diff line
@@ -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 */
+7 −2
Original line number Diff line number Diff line
@@ -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,
+8 −4
Original line number Diff line number Diff line
@@ -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);