Fix racy use of anon_inode_getfd() in perf_event.c

once anon_inode_getfd() is called, you can't expect *anything* about
struct file that descriptor points to - another thread might be doing
whatever it likes with descriptor table at that point.

Cc: stable <stable@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2010-05-26 17:40:29 -04:00
parent d7065da038
commit ea635c64e0

View File

@ -4999,8 +4999,8 @@ SYSCALL_DEFINE5(perf_event_open,
struct perf_event_context *ctx;
struct file *event_file = NULL;
struct file *group_file = NULL;
int event_fd;
int fput_needed = 0;
int fput_needed2 = 0;
int err;
/* for future expandability... */
@ -5021,12 +5021,18 @@ SYSCALL_DEFINE5(perf_event_open,
return -EINVAL;
}
event_fd = get_unused_fd_flags(O_RDWR);
if (event_fd < 0)
return event_fd;
/*
* Get the target context (task or percpu):
*/
ctx = find_get_context(pid, cpu);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
if (IS_ERR(ctx)) {
err = PTR_ERR(ctx);
goto err_fd;
}
/*
* Look up the group leader (we will attach this event to it):
@ -5066,13 +5072,11 @@ SYSCALL_DEFINE5(perf_event_open,
if (IS_ERR(event))
goto err_put_context;
err = anon_inode_getfd("[perf_event]", &perf_fops, event, O_RDWR);
if (err < 0)
goto err_free_put_context;
event_file = fget_light(err, &fput_needed2);
if (!event_file)
event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, O_RDWR);
if (IS_ERR(event_file)) {
err = PTR_ERR(event_file);
goto err_free_put_context;
}
if (flags & PERF_FLAG_FD_OUTPUT) {
err = perf_event_set_output(event, group_fd);
@ -5093,19 +5097,19 @@ SYSCALL_DEFINE5(perf_event_open,
list_add_tail(&event->owner_entry, &current->perf_event_list);
mutex_unlock(&current->perf_event_mutex);
err_fput_free_put_context:
fput_light(event_file, fput_needed2);
err_free_put_context:
if (err < 0)
free_event(event);
err_put_context:
if (err < 0)
put_ctx(ctx);
fput_light(group_file, fput_needed);
fd_install(event_fd, event_file);
return event_fd;
err_fput_free_put_context:
fput(event_file);
err_free_put_context:
free_event(event);
err_put_context:
fput_light(group_file, fput_needed);
put_ctx(ctx);
err_fd:
put_unused_fd(event_fd);
return err;
}