perf/core: Fix bad use of igrab()
commit 9511bce9fe8e5e6c0f923c09243a713eba560141 upstream As Miklos reported and suggested: "This pattern repeats two times in trace_uprobe.c and in kernel/events/core.c as well: ret = kern_path(filename, LOOKUP_FOLLOW, &path); if (ret) goto fail_address_parse; inode = igrab(d_inode(path.dentry)); path_put(&path); And it's wrong. You can only hold a reference to the inode if you have an active ref to the superblock as well (which is normally through path.mnt) or holding s_umount. This way unmounting the containing filesystem while the tracepoint is active will give you the "VFS: Busy inodes after unmount..." message and a crash when the inode is finally put. Solution: store path instead of inode." This patch fixes the issue in kernel/event/core.c. Reviewed-and-tested-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Reported-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Song Liu <songliubraving@fb.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: <kernel-team@fb.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vince Weaver <vincent.weaver@maine.edu> Fixes: 375637bc5249 ("perf/core: Introduce address range filtering") Link: http://lkml.kernel.org/r/20180418062907.3210386-2-songliubraving@fb.com Signed-off-by: Ingo Molnar <mingo@kernel.org> [sudip: Backported to 4.9: use file_inode()] Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
27979f6087
commit
51f0471b12
@ -1117,7 +1117,7 @@ static int pt_event_addr_filters_validate(struct list_head *filters)
|
||||
if (!filter->range || !filter->size)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!filter->inode) {
|
||||
if (!filter->path.dentry) {
|
||||
if (!valid_kernel_ip(filter->offset))
|
||||
return -EINVAL;
|
||||
|
||||
@ -1144,7 +1144,7 @@ static void pt_event_addr_filters_sync(struct perf_event *event)
|
||||
return;
|
||||
|
||||
list_for_each_entry(filter, &head->list, entry) {
|
||||
if (filter->inode && !offs[range]) {
|
||||
if (filter->path.dentry && !offs[range]) {
|
||||
msr_a = msr_b = 0;
|
||||
} else {
|
||||
/* apply the offset */
|
||||
|
@ -475,7 +475,7 @@ struct pmu {
|
||||
*/
|
||||
struct perf_addr_filter {
|
||||
struct list_head entry;
|
||||
struct inode *inode;
|
||||
struct path path;
|
||||
unsigned long offset;
|
||||
unsigned long size;
|
||||
unsigned int range : 1,
|
||||
|
@ -6271,7 +6271,7 @@ static void perf_event_addr_filters_exec(struct perf_event *event, void *data)
|
||||
|
||||
raw_spin_lock_irqsave(&ifh->lock, flags);
|
||||
list_for_each_entry(filter, &ifh->list, entry) {
|
||||
if (filter->inode) {
|
||||
if (filter->path.dentry) {
|
||||
event->addr_filters_offs[count] = 0;
|
||||
restart++;
|
||||
}
|
||||
@ -6814,7 +6814,7 @@ static bool perf_addr_filter_match(struct perf_addr_filter *filter,
|
||||
struct file *file, unsigned long offset,
|
||||
unsigned long size)
|
||||
{
|
||||
if (filter->inode != file->f_inode)
|
||||
if (d_inode(filter->path.dentry) != file_inode(file))
|
||||
return false;
|
||||
|
||||
if (filter->offset > offset + size)
|
||||
@ -8028,8 +8028,7 @@ static void free_filters_list(struct list_head *filters)
|
||||
struct perf_addr_filter *filter, *iter;
|
||||
|
||||
list_for_each_entry_safe(filter, iter, filters, entry) {
|
||||
if (filter->inode)
|
||||
iput(filter->inode);
|
||||
path_put(&filter->path);
|
||||
list_del(&filter->entry);
|
||||
kfree(filter);
|
||||
}
|
||||
@ -8123,7 +8122,7 @@ static void perf_event_addr_filters_apply(struct perf_event *event)
|
||||
* Adjust base offset if the filter is associated to a binary
|
||||
* that needs to be mapped:
|
||||
*/
|
||||
if (filter->inode)
|
||||
if (filter->path.dentry)
|
||||
event->addr_filters_offs[count] =
|
||||
perf_addr_filter_apply(filter, mm);
|
||||
|
||||
@ -8196,7 +8195,6 @@ perf_event_parse_addr_filter(struct perf_event *event, char *fstr,
|
||||
{
|
||||
struct perf_addr_filter *filter = NULL;
|
||||
char *start, *orig, *filename = NULL;
|
||||
struct path path;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int state = IF_STATE_ACTION, token;
|
||||
unsigned int kernel = 0;
|
||||
@ -8287,19 +8285,18 @@ perf_event_parse_addr_filter(struct perf_event *event, char *fstr,
|
||||
goto fail;
|
||||
|
||||
/* look up the path and grab its inode */
|
||||
ret = kern_path(filename, LOOKUP_FOLLOW, &path);
|
||||
ret = kern_path(filename, LOOKUP_FOLLOW,
|
||||
&filter->path);
|
||||
if (ret)
|
||||
goto fail_free_name;
|
||||
|
||||
filter->inode = igrab(d_inode(path.dentry));
|
||||
path_put(&path);
|
||||
kfree(filename);
|
||||
filename = NULL;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (!filter->inode ||
|
||||
!S_ISREG(filter->inode->i_mode))
|
||||
/* free_filters_list() will iput() */
|
||||
if (!filter->path.dentry ||
|
||||
!S_ISREG(d_inode(filter->path.dentry)
|
||||
->i_mode))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user