sys_pipe(): fix file descriptor leaks
[safe/jmp/linux-2.6] / fs / pipe.c
index f8b6bdc..ec228bc 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -16,6 +16,8 @@
 #include <linux/uio.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
+#include <linux/audit.h>
+#include <linux/syscalls.h>
 
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
@@ -44,8 +46,7 @@ void pipe_wait(struct pipe_inode_info *pipe)
         * Pipes are system-local resources, so sleeping on them
         * is considered a noninteractive wait:
         */
-       prepare_to_wait(&pipe->wait, &wait,
-                       TASK_INTERRUPTIBLE | TASK_NONINTERACTIVE);
+       prepare_to_wait(&pipe->wait, &wait, TASK_INTERRUPTIBLE);
        if (pipe->inode)
                mutex_unlock(&pipe->inode->i_mutex);
        schedule();
@@ -163,6 +164,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
                page_cache_release(page);
 }
 
+/**
+ * generic_pipe_buf_map - virtually map a pipe buffer
+ * @pipe:      the pipe that the buffer belongs to
+ * @buf:       the buffer that should be mapped
+ * @atomic:    whether to use an atomic map
+ *
+ * Description:
+ *     This function returns a kernel virtual address mapping for the
+ *     pipe_buffer passed in @buf. If @atomic is set, an atomic map is provided
+ *     and the caller has to be careful not to fault before calling
+ *     the unmap function.
+ *
+ *     Note that this function occupies KM_USER0 if @atomic != 0.
+ */
 void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
                           struct pipe_buffer *buf, int atomic)
 {
@@ -174,6 +189,15 @@ void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
        return kmap(buf->page);
 }
 
+/**
+ * generic_pipe_buf_unmap - unmap a previously mapped pipe buffer
+ * @pipe:      the pipe that the buffer belongs to
+ * @buf:       the buffer that should be unmapped
+ * @map_data:  the data that the mapping function returned
+ *
+ * Description:
+ *     This function undoes the mapping that ->map() provided.
+ */
 void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
                            struct pipe_buffer *buf, void *map_data)
 {
@@ -184,11 +208,28 @@ void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
                kunmap(buf->page);
 }
 
+/**
+ * generic_pipe_buf_steal - attempt to take ownership of a &pipe_buffer
+ * @pipe:      the pipe that the buffer belongs to
+ * @buf:       the buffer to attempt to steal
+ *
+ * Description:
+ *     This function attempts to steal the &struct page attached to
+ *     @buf. If successful, this function returns 0 and returns with
+ *     the page locked. The caller may then reuse the page for whatever
+ *     he wishes; the typical use is insertion into a different file
+ *     page cache.
+ */
 int generic_pipe_buf_steal(struct pipe_inode_info *pipe,
                           struct pipe_buffer *buf)
 {
        struct page *page = buf->page;
 
+       /*
+        * A reference of one is golden, that means that the owner of this
+        * page is the only one holding a reference to it. lock the page
+        * and return OK.
+        */
        if (page_count(page) == 1) {
                lock_page(page);
                return 0;
@@ -197,21 +238,41 @@ int generic_pipe_buf_steal(struct pipe_inode_info *pipe,
        return 1;
 }
 
-void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf)
+/**
+ * generic_pipe_buf_get - get a reference to a &struct pipe_buffer
+ * @pipe:      the pipe that the buffer belongs to
+ * @buf:       the buffer to get a reference to
+ *
+ * Description:
+ *     This function grabs an extra reference to @buf. It's used in
+ *     in the tee() system call, when we duplicate the buffers in one
+ *     pipe into another.
+ */
+void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
 {
        page_cache_get(buf->page);
 }
 
-int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf)
+/**
+ * generic_pipe_buf_confirm - verify contents of the pipe buffer
+ * @info:      the pipe that the buffer belongs to
+ * @buf:       the buffer to confirm
+ *
+ * Description:
+ *     This function does nothing, because the generic pipe code uses
+ *     pages that are always good when inserted into the pipe.
+ */
+int generic_pipe_buf_confirm(struct pipe_inode_info *info,
+                            struct pipe_buffer *buf)
 {
        return 0;
 }
 
-static struct pipe_buf_operations anon_pipe_buf_ops = {
+static const struct pipe_buf_operations anon_pipe_buf_ops = {
        .can_merge = 1,
        .map = generic_pipe_buf_map,
        .unmap = generic_pipe_buf_unmap,
-       .pin = generic_pipe_buf_pin,
+       .confirm = generic_pipe_buf_confirm,
        .release = anon_pipe_buf_release,
        .steal = generic_pipe_buf_steal,
        .get = generic_pipe_buf_get,
@@ -243,7 +304,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
                if (bufs) {
                        int curbuf = pipe->curbuf;
                        struct pipe_buffer *buf = pipe->bufs + curbuf;
-                       struct pipe_buf_operations *ops = buf->ops;
+                       const struct pipe_buf_operations *ops = buf->ops;
                        void *addr;
                        size_t chars = buf->len;
                        int error, atomic;
@@ -251,7 +312,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
                        if (chars > total_len)
                                chars = total_len;
 
-                       error = ops->pin(pipe, buf);
+                       error = ops->confirm(pipe, buf);
                        if (error) {
                                if (!ret)
                                        error = ret;
@@ -322,7 +383,7 @@ redo:
 
        /* Signal writers asynchronously that there is more room. */
        if (do_wakeup) {
-               wake_up_interruptible(&pipe->wait);
+               wake_up_interruptible_sync(&pipe->wait);
                kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
        }
        if (ret > 0)
@@ -365,14 +426,14 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov,
                int lastbuf = (pipe->curbuf + pipe->nrbufs - 1) &
                                                        (PIPE_BUFFERS-1);
                struct pipe_buffer *buf = pipe->bufs + lastbuf;
-               struct pipe_buf_operations *ops = buf->ops;
+               const struct pipe_buf_operations *ops = buf->ops;
                int offset = buf->offset + buf->len;
 
                if (ops->can_merge && offset + chars <= PAGE_SIZE) {
                        int error, atomic = 1;
                        void *addr;
 
-                       error = ops->pin(pipe, buf);
+                       error = ops->confirm(pipe, buf);
                        if (error)
                                goto out;
 
@@ -495,7 +556,7 @@ redo2:
 out:
        mutex_unlock(&inode->i_mutex);
        if (do_wakeup) {
-               wake_up_interruptible(&pipe->wait);
+               wake_up_interruptible_sync(&pipe->wait);
                kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
        }
        if (ret > 0)
@@ -516,9 +577,7 @@ bad_pipe_w(struct file *filp, const char __user *buf, size_t count,
        return -EBADF;
 }
 
-static int
-pipe_ioctl(struct inode *pino, struct file *filp,
-          unsigned int cmd, unsigned long arg)
+static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct inode *inode = filp->f_path.dentry->d_inode;
        struct pipe_inode_info *pipe;
@@ -589,7 +648,7 @@ pipe_release(struct inode *inode, int decr, int decw)
        if (!pipe->readers && !pipe->writers) {
                free_pipe_info(inode);
        } else {
-               wake_up_interruptible(&pipe->wait);
+               wake_up_interruptible_sync(&pipe->wait);
                kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
                kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
        }
@@ -725,7 +784,7 @@ const struct file_operations read_fifo_fops = {
        .aio_read       = pipe_read,
        .write          = bad_pipe_w,
        .poll           = pipe_poll,
-       .ioctl          = pipe_ioctl,
+       .unlocked_ioctl = pipe_ioctl,
        .open           = pipe_read_open,
        .release        = pipe_read_release,
        .fasync         = pipe_read_fasync,
@@ -737,7 +796,7 @@ const struct file_operations write_fifo_fops = {
        .write          = do_sync_write,
        .aio_write      = pipe_write,
        .poll           = pipe_poll,
-       .ioctl          = pipe_ioctl,
+       .unlocked_ioctl = pipe_ioctl,
        .open           = pipe_write_open,
        .release        = pipe_write_release,
        .fasync         = pipe_write_fasync,
@@ -750,44 +809,44 @@ const struct file_operations rdwr_fifo_fops = {
        .write          = do_sync_write,
        .aio_write      = pipe_write,
        .poll           = pipe_poll,
-       .ioctl          = pipe_ioctl,
+       .unlocked_ioctl = pipe_ioctl,
        .open           = pipe_rdwr_open,
        .release        = pipe_rdwr_release,
        .fasync         = pipe_rdwr_fasync,
 };
 
-static struct file_operations read_pipe_fops = {
+static const struct file_operations read_pipe_fops = {
        .llseek         = no_llseek,
        .read           = do_sync_read,
        .aio_read       = pipe_read,
        .write          = bad_pipe_w,
        .poll           = pipe_poll,
-       .ioctl          = pipe_ioctl,
+       .unlocked_ioctl = pipe_ioctl,
        .open           = pipe_read_open,
        .release        = pipe_read_release,
        .fasync         = pipe_read_fasync,
 };
 
-static struct file_operations write_pipe_fops = {
+static const struct file_operations write_pipe_fops = {
        .llseek         = no_llseek,
        .read           = bad_pipe_r,
        .write          = do_sync_write,
        .aio_write      = pipe_write,
        .poll           = pipe_poll,
-       .ioctl          = pipe_ioctl,
+       .unlocked_ioctl = pipe_ioctl,
        .open           = pipe_write_open,
        .release        = pipe_write_release,
        .fasync         = pipe_write_fasync,
 };
 
-static struct file_operations rdwr_pipe_fops = {
+static const struct file_operations rdwr_pipe_fops = {
        .llseek         = no_llseek,
        .read           = do_sync_read,
        .aio_read       = pipe_read,
        .write          = do_sync_write,
        .aio_write      = pipe_write,
        .poll           = pipe_poll,
-       .ioctl          = pipe_ioctl,
+       .unlocked_ioctl = pipe_ioctl,
        .open           = pipe_rdwr_open,
        .release        = pipe_rdwr_release,
        .fasync         = pipe_rdwr_fasync,
@@ -840,8 +899,18 @@ static int pipefs_delete_dentry(struct dentry *dentry)
        return 0;
 }
 
+/*
+ * pipefs_dname() is called from d_path().
+ */
+static char *pipefs_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+       return dynamic_dname(dentry, buffer, buflen, "pipe:[%lu]",
+                               dentry->d_inode->i_ino);
+}
+
 static struct dentry_operations pipefs_dentry_operations = {
        .d_delete       = pipefs_delete_dentry,
+       .d_dname        = pipefs_dname,
 };
 
 static struct inode * get_pipe_inode(void)
@@ -887,22 +956,15 @@ struct file *create_write_pipe(void)
        struct inode *inode;
        struct file *f;
        struct dentry *dentry;
-       char name[32];
-       struct qstr this;
+       struct qstr name = { .name = "" };
 
-       f = get_empty_filp();
-       if (!f)
-               return ERR_PTR(-ENFILE);
        err = -ENFILE;
        inode = get_pipe_inode();
        if (!inode)
-               goto err_file;
+               goto err;
 
-       this.len = sprintf(name, "[%lu]", inode->i_ino);
-       this.name = name;
-       this.hash = 0;
        err = -ENOMEM;
-       dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this);
+       dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &name);
        if (!dentry)
                goto err_inode;
 
@@ -914,29 +976,35 @@ struct file *create_write_pipe(void)
         */
        dentry->d_flags &= ~DCACHE_UNHASHED;
        d_instantiate(dentry, inode);
-       f->f_path.mnt = mntget(pipe_mnt);
-       f->f_path.dentry = dentry;
+
+       err = -ENFILE;
+       f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipe_fops);
+       if (!f)
+               goto err_dentry;
        f->f_mapping = inode->i_mapping;
 
        f->f_flags = O_WRONLY;
-       f->f_op = &write_pipe_fops;
-       f->f_mode = FMODE_WRITE;
        f->f_version = 0;
 
        return f;
 
+ err_dentry:
+       free_pipe_info(inode);
+       dput(dentry);
+       return ERR_PTR(err);
+
  err_inode:
        free_pipe_info(inode);
        iput(inode);
- err_file:
-       put_filp(f);
+ err:
        return ERR_PTR(err);
 }
 
 void free_write_pipe(struct file *f)
 {
-       mntput(f->f_path.mnt);
+       free_pipe_info(f->f_dentry->d_inode);
        dput(f->f_path.dentry);
+       mntput(f->f_path.mnt);
        put_filp(f);
 }
 
@@ -984,6 +1052,10 @@ int do_pipe(int *fd)
                goto err_fdr;
        fdw = error;
 
+       error = audit_fd_pair(fdr, fdw);
+       if (error < 0)
+               goto err_fdw;
+
        fd_install(fdr, fr);
        fd_install(fdw, fw);
        fd[0] = fdr;
@@ -991,9 +1063,13 @@ int do_pipe(int *fd)
 
        return 0;
 
+ err_fdw:
+       put_unused_fd(fdw);
  err_fdr:
        put_unused_fd(fdr);
  err_read_pipe:
+       dput(fr->f_dentry);
+       mntput(fr->f_vfsmnt);
        put_filp(fr);
  err_write_pipe:
        free_write_pipe(fw);
@@ -1001,6 +1077,26 @@ int do_pipe(int *fd)
 }
 
 /*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way Unix traditionally does this, though.
+ */
+asmlinkage long __weak sys_pipe(int __user *fildes)
+{
+       int fd[2];
+       int error;
+
+       error = do_pipe(fd);
+       if (!error) {
+               if (copy_to_user(fildes, fd, sizeof(fd))) {
+                       sys_close(fd[0]);
+                       sys_close(fd[1]);
+                       error = -EFAULT;
+               }
+       }
+       return error;
+}
+
+/*
  * pipefs should _never_ be mounted by userland - too much of security hassle,
  * no real gain from having the whole whorehouse mounted. So we don't need
  * any operations on the root directory. However, we need a non-trivial