Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

trace_uprobe/sdt: Prevent multiple reference counter for same uprobe

We assume to have only one reference counter for one uprobe.
Don't allow user to add multiple trace_uprobe entries having
same inode+offset but different reference counter.

Ex,
# echo "p:sdt_tick/loop2 /home/ravi/tick:0x6e4(0x10036)" > uprobe_events
# echo "p:sdt_tick/loop2_1 /home/ravi/tick:0x6e4(0xfffff)" >> uprobe_events
bash: echo: write error: Invalid argument

# dmesg
trace_kprobe: Reference counter offset mismatch.

There is one exception though:
When user is trying to replace the old entry with the new
one, we allow this if the new entry does not conflict with
any other existing entries.

Link: http://lkml.kernel.org/r/20180820044250.11659-4-ravi.bangoria@linux.ibm.com

Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Reviewed-by: Song Liu <songliubraving@fb.com>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Tested-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>

authored by

Ravi Bangoria and committed by
Steven Rostedt (VMware)
ccea8727 22bad382

+35 -2
+35 -2
kernel/trace/trace_uprobe.c
··· 312 312 return 0; 313 313 } 314 314 315 + /* 316 + * Uprobe with multiple reference counter is not allowed. i.e. 317 + * If inode and offset matches, reference counter offset *must* 318 + * match as well. Though, there is one exception: If user is 319 + * replacing old trace_uprobe with new one(same group/event), 320 + * then we allow same uprobe with new reference counter as far 321 + * as the new one does not conflict with any other existing 322 + * ones. 323 + */ 324 + static struct trace_uprobe *find_old_trace_uprobe(struct trace_uprobe *new) 325 + { 326 + struct trace_uprobe *tmp, *old = NULL; 327 + struct inode *new_inode = d_real_inode(new->path.dentry); 328 + 329 + old = find_probe_event(trace_event_name(&new->tp.call), 330 + new->tp.call.class->system); 331 + 332 + list_for_each_entry(tmp, &uprobe_list, list) { 333 + if ((old ? old != tmp : true) && 334 + new_inode == d_real_inode(tmp->path.dentry) && 335 + new->offset == tmp->offset && 336 + new->ref_ctr_offset != tmp->ref_ctr_offset) { 337 + pr_warn("Reference counter offset mismatch."); 338 + return ERR_PTR(-EINVAL); 339 + } 340 + } 341 + return old; 342 + } 343 + 315 344 /* Register a trace_uprobe and probe_event */ 316 345 static int register_trace_uprobe(struct trace_uprobe *tu) 317 346 { ··· 350 321 mutex_lock(&uprobe_lock); 351 322 352 323 /* register as an event */ 353 - old_tu = find_probe_event(trace_event_name(&tu->tp.call), 354 - tu->tp.call.class->system); 324 + old_tu = find_old_trace_uprobe(tu); 325 + if (IS_ERR(old_tu)) { 326 + ret = PTR_ERR(old_tu); 327 + goto end; 328 + } 329 + 355 330 if (old_tu) { 356 331 /* delete old event */ 357 332 ret = unregister_trace_uprobe(old_tu);