Skip to content

Commit 99c9a92

Browse files
mhiramatrostedt
authored andcommitted
tracing/uprobe: Fix double perf_event linking on multiprobe uprobe
Fix double perf_event linking to trace_uprobe_filter on multiple uprobe event by moving trace_uprobe_filter under trace_probe_event. In uprobe perf event, trace_uprobe_filter data structure is managing target mm filters (in perf_event) related to each uprobe event. Since commit 60d53e2 ("tracing/probe: Split trace_event related data from trace_probe") left the trace_uprobe_filter data structure in trace_uprobe, if a trace_probe_event has multiple trace_uprobe (multi-probe event), a perf_event is added to different trace_uprobe_filter on each trace_uprobe. This leads a linked list corruption. To fix this issue, move trace_uprobe_filter to trace_probe_event and link it once on each event instead of each probe. Link: http://lkml.kernel.org/r/157862073931.1800.3800576241181489174.stgit@devnote2 Cc: Jiri Olsa <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: "Naveen N . Rao" <[email protected]> Cc: Anil S Keshavamurthy <[email protected]> Cc: "David S . Miller" <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: =?utf-8?q?Toke_H=C3=B8iland-J?= =?utf-8?b?w7hyZ2Vuc2Vu?= <[email protected]> Cc: Jean-Tsung Hsiao <[email protected]> Cc: Jesper Dangaard Brouer <[email protected]> Cc: [email protected] Fixes: 60d53e2 ("tracing/probe: Split trace_event related data from trace_probe") Link: https://lkml.kernel.org/r/[email protected] Reported-by: Arnaldo Carvalho de Melo <[email protected]> Tested-by: Arnaldo Carvalho de Melo <[email protected]> Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent d0695e2 commit 99c9a92

File tree

4 files changed

+86
-48
lines changed

4 files changed

+86
-48
lines changed

kernel/trace/trace_kprobe.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ static struct trace_kprobe *alloc_trace_kprobe(const char *group,
290290
INIT_HLIST_NODE(&tk->rp.kp.hlist);
291291
INIT_LIST_HEAD(&tk->rp.kp.list);
292292

293-
ret = trace_probe_init(&tk->tp, event, group);
293+
ret = trace_probe_init(&tk->tp, event, group, 0);
294294
if (ret < 0)
295295
goto error;
296296

kernel/trace/trace_probe.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -984,15 +984,16 @@ void trace_probe_cleanup(struct trace_probe *tp)
984984
}
985985

986986
int trace_probe_init(struct trace_probe *tp, const char *event,
987-
const char *group)
987+
const char *group, size_t event_data_size)
988988
{
989989
struct trace_event_call *call;
990990
int ret = 0;
991991

992992
if (!event || !group)
993993
return -EINVAL;
994994

995-
tp->event = kzalloc(sizeof(struct trace_probe_event), GFP_KERNEL);
995+
tp->event = kzalloc(sizeof(struct trace_probe_event) + event_data_size,
996+
GFP_KERNEL);
996997
if (!tp->event)
997998
return -ENOMEM;
998999

kernel/trace/trace_probe.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ struct trace_probe_event {
230230
struct trace_event_call call;
231231
struct list_head files;
232232
struct list_head probes;
233+
char data[0];
233234
};
234235

235236
struct trace_probe {
@@ -322,7 +323,7 @@ static inline bool trace_probe_has_single_file(struct trace_probe *tp)
322323
}
323324

324325
int trace_probe_init(struct trace_probe *tp, const char *event,
325-
const char *group);
326+
const char *group, size_t event_data_size);
326327
void trace_probe_cleanup(struct trace_probe *tp);
327328
int trace_probe_append(struct trace_probe *tp, struct trace_probe *to);
328329
void trace_probe_unlink(struct trace_probe *tp);

kernel/trace/trace_uprobe.c

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ static struct dyn_event_operations trace_uprobe_ops = {
6060
*/
6161
struct trace_uprobe {
6262
struct dyn_event devent;
63-
struct trace_uprobe_filter filter;
6463
struct uprobe_consumer consumer;
6564
struct path path;
6665
struct inode *inode;
@@ -264,6 +263,14 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
264263
}
265264
NOKPROBE_SYMBOL(process_fetch_insn)
266265

266+
static struct trace_uprobe_filter *
267+
trace_uprobe_get_filter(struct trace_uprobe *tu)
268+
{
269+
struct trace_probe_event *event = tu->tp.event;
270+
271+
return (struct trace_uprobe_filter *)&event->data[0];
272+
}
273+
267274
static inline void init_trace_uprobe_filter(struct trace_uprobe_filter *filter)
268275
{
269276
rwlock_init(&filter->rwlock);
@@ -351,15 +358,16 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)
351358
if (!tu)
352359
return ERR_PTR(-ENOMEM);
353360

354-
ret = trace_probe_init(&tu->tp, event, group);
361+
ret = trace_probe_init(&tu->tp, event, group,
362+
sizeof(struct trace_uprobe_filter));
355363
if (ret < 0)
356364
goto error;
357365

358366
dyn_event_init(&tu->devent, &trace_uprobe_ops);
359367
tu->consumer.handler = uprobe_dispatcher;
360368
if (is_ret)
361369
tu->consumer.ret_handler = uretprobe_dispatcher;
362-
init_trace_uprobe_filter(&tu->filter);
370+
init_trace_uprobe_filter(trace_uprobe_get_filter(tu));
363371
return tu;
364372

365373
error:
@@ -1067,13 +1075,14 @@ static void __probe_event_disable(struct trace_probe *tp)
10671075
struct trace_probe *pos;
10681076
struct trace_uprobe *tu;
10691077

1078+
tu = container_of(tp, struct trace_uprobe, tp);
1079+
WARN_ON(!uprobe_filter_is_empty(trace_uprobe_get_filter(tu)));
1080+
10701081
list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
10711082
tu = container_of(pos, struct trace_uprobe, tp);
10721083
if (!tu->inode)
10731084
continue;
10741085

1075-
WARN_ON(!uprobe_filter_is_empty(&tu->filter));
1076-
10771086
uprobe_unregister(tu->inode, tu->offset, &tu->consumer);
10781087
tu->inode = NULL;
10791088
}
@@ -1108,7 +1117,7 @@ static int probe_event_enable(struct trace_event_call *call,
11081117
}
11091118

11101119
tu = container_of(tp, struct trace_uprobe, tp);
1111-
WARN_ON(!uprobe_filter_is_empty(&tu->filter));
1120+
WARN_ON(!uprobe_filter_is_empty(trace_uprobe_get_filter(tu)));
11121121

11131122
if (enabled)
11141123
return 0;
@@ -1205,39 +1214,39 @@ __uprobe_perf_filter(struct trace_uprobe_filter *filter, struct mm_struct *mm)
12051214
}
12061215

12071216
static inline bool
1208-
uprobe_filter_event(struct trace_uprobe *tu, struct perf_event *event)
1217+
trace_uprobe_filter_event(struct trace_uprobe_filter *filter,
1218+
struct perf_event *event)
12091219
{
1210-
return __uprobe_perf_filter(&tu->filter, event->hw.target->mm);
1220+
return __uprobe_perf_filter(filter, event->hw.target->mm);
12111221
}
12121222

1213-
static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event)
1223+
static bool trace_uprobe_filter_remove(struct trace_uprobe_filter *filter,
1224+
struct perf_event *event)
12141225
{
12151226
bool done;
12161227

1217-
write_lock(&tu->filter.rwlock);
1228+
write_lock(&filter->rwlock);
12181229
if (event->hw.target) {
12191230
list_del(&event->hw.tp_list);
1220-
done = tu->filter.nr_systemwide ||
1231+
done = filter->nr_systemwide ||
12211232
(event->hw.target->flags & PF_EXITING) ||
1222-
uprobe_filter_event(tu, event);
1233+
trace_uprobe_filter_event(filter, event);
12231234
} else {
1224-
tu->filter.nr_systemwide--;
1225-
done = tu->filter.nr_systemwide;
1235+
filter->nr_systemwide--;
1236+
done = filter->nr_systemwide;
12261237
}
1227-
write_unlock(&tu->filter.rwlock);
1228-
1229-
if (!done)
1230-
return uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
1238+
write_unlock(&filter->rwlock);
12311239

1232-
return 0;
1240+
return done;
12331241
}
12341242

1235-
static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event)
1243+
/* This returns true if the filter always covers target mm */
1244+
static bool trace_uprobe_filter_add(struct trace_uprobe_filter *filter,
1245+
struct perf_event *event)
12361246
{
12371247
bool done;
1238-
int err;
12391248

1240-
write_lock(&tu->filter.rwlock);
1249+
write_lock(&filter->rwlock);
12411250
if (event->hw.target) {
12421251
/*
12431252
* event->parent != NULL means copy_process(), we can avoid
@@ -1247,28 +1256,21 @@ static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event)
12471256
* attr.enable_on_exec means that exec/mmap will install the
12481257
* breakpoints we need.
12491258
*/
1250-
done = tu->filter.nr_systemwide ||
1259+
done = filter->nr_systemwide ||
12511260
event->parent || event->attr.enable_on_exec ||
1252-
uprobe_filter_event(tu, event);
1253-
list_add(&event->hw.tp_list, &tu->filter.perf_events);
1261+
trace_uprobe_filter_event(filter, event);
1262+
list_add(&event->hw.tp_list, &filter->perf_events);
12541263
} else {
1255-
done = tu->filter.nr_systemwide;
1256-
tu->filter.nr_systemwide++;
1264+
done = filter->nr_systemwide;
1265+
filter->nr_systemwide++;
12571266
}
1258-
write_unlock(&tu->filter.rwlock);
1267+
write_unlock(&filter->rwlock);
12591268

1260-
err = 0;
1261-
if (!done) {
1262-
err = uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
1263-
if (err)
1264-
uprobe_perf_close(tu, event);
1265-
}
1266-
return err;
1269+
return done;
12671270
}
12681271

1269-
static int uprobe_perf_multi_call(struct trace_event_call *call,
1270-
struct perf_event *event,
1271-
int (*op)(struct trace_uprobe *tu, struct perf_event *event))
1272+
static int uprobe_perf_close(struct trace_event_call *call,
1273+
struct perf_event *event)
12721274
{
12731275
struct trace_probe *pos, *tp;
12741276
struct trace_uprobe *tu;
@@ -1278,25 +1280,59 @@ static int uprobe_perf_multi_call(struct trace_event_call *call,
12781280
if (WARN_ON_ONCE(!tp))
12791281
return -ENODEV;
12801282

1283+
tu = container_of(tp, struct trace_uprobe, tp);
1284+
if (trace_uprobe_filter_remove(trace_uprobe_get_filter(tu), event))
1285+
return 0;
1286+
12811287
list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
12821288
tu = container_of(pos, struct trace_uprobe, tp);
1283-
ret = op(tu, event);
1289+
ret = uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
12841290
if (ret)
12851291
break;
12861292
}
12871293

12881294
return ret;
12891295
}
1296+
1297+
static int uprobe_perf_open(struct trace_event_call *call,
1298+
struct perf_event *event)
1299+
{
1300+
struct trace_probe *pos, *tp;
1301+
struct trace_uprobe *tu;
1302+
int err = 0;
1303+
1304+
tp = trace_probe_primary_from_call(call);
1305+
if (WARN_ON_ONCE(!tp))
1306+
return -ENODEV;
1307+
1308+
tu = container_of(tp, struct trace_uprobe, tp);
1309+
if (trace_uprobe_filter_add(trace_uprobe_get_filter(tu), event))
1310+
return 0;
1311+
1312+
list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
1313+
err = uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
1314+
if (err) {
1315+
uprobe_perf_close(call, event);
1316+
break;
1317+
}
1318+
}
1319+
1320+
return err;
1321+
}
1322+
12901323
static bool uprobe_perf_filter(struct uprobe_consumer *uc,
12911324
enum uprobe_filter_ctx ctx, struct mm_struct *mm)
12921325
{
1326+
struct trace_uprobe_filter *filter;
12931327
struct trace_uprobe *tu;
12941328
int ret;
12951329

12961330
tu = container_of(uc, struct trace_uprobe, consumer);
1297-
read_lock(&tu->filter.rwlock);
1298-
ret = __uprobe_perf_filter(&tu->filter, mm);
1299-
read_unlock(&tu->filter.rwlock);
1331+
filter = trace_uprobe_get_filter(tu);
1332+
1333+
read_lock(&filter->rwlock);
1334+
ret = __uprobe_perf_filter(filter, mm);
1335+
read_unlock(&filter->rwlock);
13001336

13011337
return ret;
13021338
}
@@ -1419,10 +1455,10 @@ trace_uprobe_register(struct trace_event_call *event, enum trace_reg type,
14191455
return 0;
14201456

14211457
case TRACE_REG_PERF_OPEN:
1422-
return uprobe_perf_multi_call(event, data, uprobe_perf_open);
1458+
return uprobe_perf_open(event, data);
14231459

14241460
case TRACE_REG_PERF_CLOSE:
1425-
return uprobe_perf_multi_call(event, data, uprobe_perf_close);
1461+
return uprobe_perf_close(event, data);
14261462

14271463
#endif
14281464
default:

0 commit comments

Comments
 (0)