From patchwork Sat Oct 29 16:19:55 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Windsor X-Patchwork-Id: 9403971 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 9868160588 for ; Sat, 29 Oct 2016 16:21:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8A3992909D for ; Sat, 29 Oct 2016 16:21:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7D990290D2; Sat, 29 Oct 2016 16:21:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id EA24B2909D for ; Sat, 29 Oct 2016 16:21:06 +0000 (UTC) Received: (qmail 26091 invoked by uid 550); 29 Oct 2016 16:20:37 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Reply-To: kernel-hardening@lists.openwall.com Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 25958 invoked from network); 29 Oct 2016 16:20:35 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=yTLZnTaFYAshM2iLBXN97zilWCAwRVB+QvmtrRt5F+g=; b=OmmBknRC7BopXB3d5d+MNHtCPAG7TAsHsvoXQqrDY9ypJ0G6WMCKWqDgCCb8dk3GcW 8M9BqvGYdNNxCpv86WtncMnVBg2zD1bq3HrdG+5s7tyrvSqOPYF3knn4ZnfYH771ydDf zU9Gzr1Cnr/MKSQtq01wvn8wvWoRUJMWa06FgGSddSWhgL8ab8hpunWQ309G1iLcOIFa IClK6wQNBWDjRvYS1z6Jl5bk9cCe/DPF6h1ERj4tht5g8QQ78/4ZhnOTkIx242r5/pm5 Q89RNAw3tckqUTZ6h9ZU3XkEZJAAu1NxsXHFqZlsqA4yYdM4ac/re/pfpsprOa28XDJr jiFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=yTLZnTaFYAshM2iLBXN97zilWCAwRVB+QvmtrRt5F+g=; b=nFsG17df+V28Fir/QbI2z/ItNvWl9neiWOmQelPjWf8iDsi7CKu8Eyxh9ATGAndcrU RYGcVk1ziL3NpVvSGOdstKPqCB2Sy5JY1Pkh0/sMwnORcPWu2Gkj/QpACUmhmHHKM+qL eAsJ+0lVgnEYq2+QOWt3WdDlA1nCYzD/URS7g+Z6/knZW4i27+6eGtGQkyguUK8fYW6g VZ3r0chtklyIZ/hHdQSnYaA8vQ1ro+Z8U09KIpL+M2kceFpE4QZCw6RtP6o4ryZFNagh 1o/kzjph7C2Pt03gKS5M2uVavyGiU6YPmRuO5S6cwbzPq/MroxgTakXXLe4Z8PTg1FST UKig== X-Gm-Message-State: ABUngveUV2cGhLzWIQO6z6coeL9QMuVXtBtcR5+IW1NuMdza0Hq+lMLfXEa40ey6FBqCqw== X-Received: by 10.200.39.140 with SMTP id w12mr15541679qtw.28.1477758023697; Sat, 29 Oct 2016 09:20:23 -0700 (PDT) From: David Windsor To: kernel-hardening@lists.openwall.com Cc: keescook@chromium.org, elena.reshetova@intel.com, ishkamiel@gmail.com, takahiro.akashi@linaro.org, colin@cvidal.org, dwindsor@gmail.com Date: Sat, 29 Oct 2016 12:19:55 -0400 Message-Id: <1477757996-22468-5-git-send-email-dwindsor@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1477757996-22468-1-git-send-email-dwindsor@gmail.com> References: <1477757996-22468-1-git-send-email-dwindsor@gmail.com> Subject: [kernel-hardening][RFC PATCH 4/5] fs: add overflow protection to struct pipe_inode_info.{readers|writers|files|waiting_writers} X-Virus-Scanned: ClamAV using ClamSMTP Change type of struct pipe_inode_info.{readers|writers|files|waiting_writers} to atomic_t. This enables overflow protection: when CONFIG_HARDENED_ATOMIC is enabled, atomic_t variables cannot be overflowed. The copyright for the original PAX_REFCOUNT code: - all REFCOUNT code in general: PaX Team - various false positive fixes: Mathias Krause --- fs/coredump.c | 10 ++++---- fs/pipe.c | 59 ++++++++++++++++++++++++----------------------- fs/splice.c | 36 ++++++++++++++--------------- include/linux/pipe_fs_i.h | 8 +++---- 4 files changed, 57 insertions(+), 56 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 8d323b4..4ae7b89 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -483,8 +483,8 @@ static void wait_for_dump_helpers(struct file *file) struct pipe_inode_info *pipe = file->private_data; pipe_lock(pipe); - pipe->readers++; - pipe->writers--; + atomic_inc(&pipe->readers); + atomic_dec(&pipe->writers); wake_up_interruptible_sync(&pipe->wait); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); pipe_unlock(pipe); @@ -493,11 +493,11 @@ static void wait_for_dump_helpers(struct file *file) * We actually want wait_event_freezable() but then we need * to clear TIF_SIGPENDING and improve dump_interrupted(). */ - wait_event_interruptible(pipe->wait, pipe->readers == 1); + wait_event_interruptible(pipe->wait, atomic_read(&pipe->readers) == 1); pipe_lock(pipe); - pipe->readers--; - pipe->writers++; + atomic_dec(&pipe->readers); + atomic_inc(&pipe->writers); pipe_unlock(pipe); } diff --git a/fs/pipe.c b/fs/pipe.c index 8e0d9f2..ceea372 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -62,7 +62,7 @@ unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR; static void pipe_lock_nested(struct pipe_inode_info *pipe, int subclass) { - if (pipe->files) + if (atomic_read(&pipe->files)) mutex_lock_nested(&pipe->mutex, subclass); } @@ -77,7 +77,7 @@ EXPORT_SYMBOL(pipe_lock); void pipe_unlock(struct pipe_inode_info *pipe) { - if (pipe->files) + if (atomic_read(&pipe->files)) mutex_unlock(&pipe->mutex); } EXPORT_SYMBOL(pipe_unlock); @@ -310,9 +310,9 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to) } if (bufs) /* More to do? */ continue; - if (!pipe->writers) + if (!atomic_read(&pipe->writers)) break; - if (!pipe->waiting_writers) { + if (!atomic_read(&pipe->waiting_writers)) { /* syscall merging: Usually we must not sleep * if O_NONBLOCK is set, or if we got some data. * But if a writer sleeps in kernel space, then @@ -369,7 +369,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) __pipe_lock(pipe); - if (!pipe->readers) { + if (!atomic_read(&pipe->readers)) { send_sig(SIGPIPE, current, 0); ret = -EPIPE; goto out; @@ -403,7 +403,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) for (;;) { int bufs; - if (!pipe->readers) { + if (!atomic_read(&pipe->readers)) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; @@ -471,9 +471,9 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); do_wakeup = 0; } - pipe->waiting_writers++; + atomic_inc(&pipe->waiting_writers); pipe_wait(pipe); - pipe->waiting_writers--; + atomic_dec(&pipe->waiting_writers); } out: __pipe_unlock(pipe); @@ -528,7 +528,7 @@ pipe_poll(struct file *filp, poll_table *wait) mask = 0; if (filp->f_mode & FMODE_READ) { mask = (nrbufs > 0) ? POLLIN | POLLRDNORM : 0; - if (!pipe->writers && filp->f_version != pipe->w_counter) + if (!atomic_read(&pipe->writers) && filp->f_version != pipe->w_counter) mask |= POLLHUP; } @@ -538,7 +538,7 @@ pipe_poll(struct file *filp, poll_table *wait) * Most Unices do not set POLLERR for FIFOs but on Linux they * behave exactly like pipes for poll(). */ - if (!pipe->readers) + if (!atomic_read(&pipe->readers)) mask |= POLLERR; } @@ -550,7 +550,7 @@ static void put_pipe_info(struct inode *inode, struct pipe_inode_info *pipe) int kill = 0; spin_lock(&inode->i_lock); - if (!--pipe->files) { + if (atomic_dec_and_test(&pipe->files)) { inode->i_pipe = NULL; kill = 1; } @@ -567,11 +567,11 @@ pipe_release(struct inode *inode, struct file *file) __pipe_lock(pipe); if (file->f_mode & FMODE_READ) - pipe->readers--; + atomic_dec(&pipe->readers); if (file->f_mode & FMODE_WRITE) - pipe->writers--; + atomic_dec(&pipe->writers); - if (pipe->readers || pipe->writers) { + if (atomic_read(&pipe->readers) || atomic_read(&pipe->writers)) { wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM | POLLERR | POLLHUP); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); @@ -708,8 +708,9 @@ static struct inode * get_pipe_inode(void) goto fail_iput; inode->i_pipe = pipe; - pipe->files = 2; - pipe->readers = pipe->writers = 1; + atomic_set(&pipe->files, 2); + atomic_set(&pipe->readers, 1); + atomic_set(&pipe->writers, 1); inode->i_fop = &pipefifo_fops; /* @@ -891,17 +892,17 @@ static int fifo_open(struct inode *inode, struct file *filp) spin_lock(&inode->i_lock); if (inode->i_pipe) { pipe = inode->i_pipe; - pipe->files++; + atomic_inc(&pipe->files); spin_unlock(&inode->i_lock); } else { spin_unlock(&inode->i_lock); pipe = alloc_pipe_info(); if (!pipe) return -ENOMEM; - pipe->files = 1; + atomic_set(&pipe->files, 1); spin_lock(&inode->i_lock); if (unlikely(inode->i_pipe)) { - inode->i_pipe->files++; + atomic_inc(&inode->i_pipe->files); spin_unlock(&inode->i_lock); free_pipe_info(pipe); pipe = inode->i_pipe; @@ -926,10 +927,10 @@ static int fifo_open(struct inode *inode, struct file *filp) * opened, even when there is no process writing the FIFO. */ pipe->r_counter++; - if (pipe->readers++ == 0) + if (atomic_inc_return(&pipe->readers) == 1) wake_up_partner(pipe); - if (!is_pipe && !pipe->writers) { + if (!is_pipe && !atomic_read(&pipe->writers)) { if ((filp->f_flags & O_NONBLOCK)) { /* suppress POLLHUP until we have * seen a writer */ @@ -948,14 +949,14 @@ static int fifo_open(struct inode *inode, struct file *filp) * errno=ENXIO when there is no process reading the FIFO. */ ret = -ENXIO; - if (!is_pipe && (filp->f_flags & O_NONBLOCK) && !pipe->readers) + if (!is_pipe && (filp->f_flags & O_NONBLOCK) && !atomic_read(&pipe->readers)) goto err; pipe->w_counter++; - if (!pipe->writers++) + if (atomic_inc_return(&pipe->writers) == 1) wake_up_partner(pipe); - if (!is_pipe && !pipe->readers) { + if (!is_pipe && !atomic_read(&pipe->readers)) { if (wait_for_partner(pipe, &pipe->r_counter)) goto err_wr; } @@ -969,11 +970,11 @@ static int fifo_open(struct inode *inode, struct file *filp) * the process can at least talk to itself. */ - pipe->readers++; - pipe->writers++; + atomic_read(&pipe->readers); + atomic_inc(&pipe->writers); pipe->r_counter++; pipe->w_counter++; - if (pipe->readers == 1 || pipe->writers == 1) + if (atomic_read(&pipe->readers) == 1 || atomic_read(&pipe->writers) == 1) wake_up_partner(pipe); break; @@ -987,13 +988,13 @@ static int fifo_open(struct inode *inode, struct file *filp) return 0; err_rd: - if (!--pipe->readers) + if (atomic_dec_and_test(&pipe->readers)) wake_up_interruptible(&pipe->wait); ret = -ERESTARTSYS; goto err; err_wr: - if (!--pipe->writers) + if (atomic_dec_and_test(&pipe->writers)) wake_up_interruptible(&pipe->wait); ret = -ERESTARTSYS; goto err; diff --git a/fs/splice.c b/fs/splice.c index 153d4f3..8376baa 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -188,7 +188,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe, if (!spd_pages) return 0; - if (unlikely(!pipe->readers)) { + if (unlikely(!atomic_read(&pipe->readers))) { send_sig(SIGPIPE, current, 0); ret = -EPIPE; goto out; @@ -227,7 +227,7 @@ ssize_t add_to_pipe(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { int ret; - if (unlikely(!pipe->readers)) { + if (unlikely(!atomic_read(&pipe->readers))) { send_sig(SIGPIPE, current, 0); ret = -EPIPE; } else if (pipe->nrbufs == pipe->buffers) { @@ -537,7 +537,7 @@ static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_des pipe_buf_release(pipe, buf); pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); pipe->nrbufs--; - if (pipe->files) + if (atomic_read(&pipe->files)) sd->need_wakeup = true; } @@ -568,10 +568,10 @@ static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_des return -ERESTARTSYS; while (!pipe->nrbufs) { - if (!pipe->writers) + if (!atomic_read(&pipe->writers)) return 0; - if (!pipe->waiting_writers && sd->num_spliced) + if (!atomic_read(&pipe->waiting_writers) && sd->num_spliced) return 0; if (sd->flags & SPLICE_F_NONBLOCK) @@ -785,7 +785,7 @@ iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out, pipe_buf_release(pipe, buf); pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); pipe->nrbufs--; - if (pipe->files) + if (atomic_read(&pipe->files)) sd.need_wakeup = true; } else { buf->offset += ret; @@ -948,7 +948,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, * out of the pipe right after the splice_to_pipe(). So set * PIPE_READERS appropriately. */ - pipe->readers = 1; + atomic_set(&pipe->readers, 1); current->splice_pipe = pipe; } @@ -1095,9 +1095,9 @@ static int wait_for_space(struct pipe_inode_info *pipe, unsigned flags) return -EAGAIN; if (signal_pending(current)) return -ERESTARTSYS; - pipe->waiting_writers++; + atomic_inc(&pipe->waiting_writers); pipe_wait(pipe); - pipe->waiting_writers--; + atomic_dec(&pipe->waiting_writers); } return 0; } @@ -1445,9 +1445,9 @@ static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags) ret = -ERESTARTSYS; break; } - if (!pipe->writers) + if (!atomic_read(&pipe->writers)) break; - if (!pipe->waiting_writers) { + if (!atomic_read(&pipe->waiting_writers)) { if (flags & SPLICE_F_NONBLOCK) { ret = -EAGAIN; break; @@ -1479,7 +1479,7 @@ static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags) pipe_lock(pipe); while (pipe->nrbufs >= pipe->buffers) { - if (!pipe->readers) { + if (!atomic_read(&pipe->readers)) { send_sig(SIGPIPE, current, 0); ret = -EPIPE; break; @@ -1492,9 +1492,9 @@ static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags) ret = -ERESTARTSYS; break; } - pipe->waiting_writers++; + atomic_inc(&pipe->waiting_writers); pipe_wait(pipe); - pipe->waiting_writers--; + atomic_dec(&pipe->waiting_writers); } pipe_unlock(pipe); @@ -1530,14 +1530,14 @@ static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, pipe_double_lock(ipipe, opipe); do { - if (!opipe->readers) { + if (!atomic_read(&opipe->readers)) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; break; } - if (!ipipe->nrbufs && !ipipe->writers) + if (!ipipe->nrbufs && !atomic_read(&ipipe->writers)) break; /* @@ -1634,7 +1634,7 @@ static int link_pipe(struct pipe_inode_info *ipipe, pipe_double_lock(ipipe, opipe); do { - if (!opipe->readers) { + if (!atomic_read(&opipe->readers)) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; @@ -1679,7 +1679,7 @@ static int link_pipe(struct pipe_inode_info *ipipe, * return EAGAIN if we have the potential of some data in the * future, otherwise just return 0 */ - if (!ret && ipipe->waiting_writers && (flags & SPLICE_F_NONBLOCK)) + if (!ret && atomic_read(&ipipe->waiting_writers) && (flags & SPLICE_F_NONBLOCK)) ret = -EAGAIN; pipe_unlock(ipipe); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index e7497c9..43ebf07 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -48,10 +48,10 @@ struct pipe_inode_info { struct mutex mutex; wait_queue_head_t wait; unsigned int nrbufs, curbuf, buffers; - unsigned int readers; - unsigned int writers; - unsigned int files; - unsigned int waiting_writers; + atomic_t readers; + atomic_t writers; + atomic_t files; + atomic_t waiting_writers; unsigned int r_counter; unsigned int w_counter; struct page *tmp_page;