]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Fix racy use of anon_inode_getfd() in perf_event.c
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 26 May 2010 21:40:29 +0000 (17:40 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 5 Jul 2010 18:21:55 +0000 (11:21 -0700)
commit ea635c64e007061f6468ece5cc9cc62d41d4ecf2 upstream.

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.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
kernel/perf_event.c

index 78c36c5a4fe5a69798ba23911549355cc16b2cb2..44b4c626907cab07b2f5a0669b6823935c79b2c0 100644 (file)
@@ -4811,8 +4811,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... */
@@ -4833,12 +4833,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):
@@ -4878,13 +4884,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);
@@ -4905,19 +4909,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);
+       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:
-       if (err < 0)
-               free_event(event);
-
+       free_event(event);
 err_put_context:
-       if (err < 0)
-               put_ctx(ctx);
-
        fput_light(group_file, fput_needed);
-
+       put_ctx(ctx);
+err_fd:
+       put_unused_fd(event_fd);
        return err;
 }