From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 43C70FC6197 for ; Fri, 8 Nov 2019 09:38:29 +0000 (UTC) Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by mail.kernel.org (Postfix) with ESMTP id F014421D7E for ; Fri, 8 Nov 2019 09:38:28 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org F014421D7E Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=suse.cz Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=owner-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix) id 472346B0006; Fri, 8 Nov 2019 04:38:27 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 3FC9D6B0007; Fri, 8 Nov 2019 04:38:27 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 2C3126B0008; Fri, 8 Nov 2019 04:38:27 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0011.hostedemail.com [216.40.44.11]) by kanga.kvack.org (Postfix) with ESMTP id 0E50B6B0007 for ; Fri, 8 Nov 2019 04:38:27 -0500 (EST) Received: from smtpin15.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay01.hostedemail.com (Postfix) with SMTP id BAEA7180AD806 for ; Fri, 8 Nov 2019 09:38:26 +0000 (UTC) X-FDA: 76132609812.15.stem32_83e2003958508 X-HE-Tag: stem32_83e2003958508 X-Filterd-Recvd-Size: 6649 Received: from mx1.suse.de (mx2.suse.de [195.135.220.15]) by imf18.hostedemail.com (Postfix) with ESMTP for ; Fri, 8 Nov 2019 09:38:26 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 6A198AE4B; Fri, 8 Nov 2019 09:38:24 +0000 (UTC) From: Vlastimil Babka To: stable@vger.kernel.org Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, Ajay Kaher , Matthew Wilcox , Jann Horn , stable@kernel.org, Linus Torvalds , Vlastimil Babka Subject: [PATCH STABLE 4.4 7/8] fs: prevent page refcount overflow in pipe_buf_get Date: Fri, 8 Nov 2019 10:38:13 +0100 Message-Id: <20191108093814.16032-8-vbabka@suse.cz> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191108093814.16032-1-vbabka@suse.cz> References: <20191108093814.16032-1-vbabka@suse.cz> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: From: Matthew Wilcox commit 15fab63e1e57be9fdb5eec1bbc5916e9825e9acb upstream. Change pipe_buf_get() to return a bool indicating whether it succeeded in raising the refcount of the page (if the thing in the pipe is a page). This removes another mechanism for overflowing the page refcount. All callers converted to handle a failure. Reported-by: Jann Horn Signed-off-by: Matthew Wilcox Cc: stable@kernel.org Signed-off-by: Linus Torvalds Signed-off-by: Vlastimil Babka --- fs/fuse/dev.c | 12 ++++++------ fs/pipe.c | 4 ++-- fs/splice.c | 12 ++++++++++-- include/linux/pipe_fs_i.h | 10 ++++++---- kernel/trace/trace.c | 6 +++++- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 36a5df92eb9c..16891f5364af 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2031,10 +2031,8 @@ static ssize_t fuse_dev_splice_write(struct pipe_i= node_info *pipe, rem +=3D pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len; =20 ret =3D -EINVAL; - if (rem < len) { - pipe_unlock(pipe); - goto out; - } + if (rem < len) + goto out_free; =20 rem =3D len; while (rem) { @@ -2052,7 +2050,9 @@ static ssize_t fuse_dev_splice_write(struct pipe_in= ode_info *pipe, pipe->curbuf =3D (pipe->curbuf + 1) & (pipe->buffers - 1); pipe->nrbufs--; } else { - pipe_buf_get(pipe, ibuf); + if (!pipe_buf_get(pipe, ibuf)) + goto out_free; + *obuf =3D *ibuf; obuf->flags &=3D ~PIPE_BUF_FLAG_GIFT; obuf->len =3D rem; @@ -2075,13 +2075,13 @@ static ssize_t fuse_dev_splice_write(struct pipe_= inode_info *pipe, ret =3D fuse_dev_do_write(fud, &cs, len); =20 pipe_lock(pipe); +out_free: for (idx =3D 0; idx < nbuf; idx++) { struct pipe_buffer *buf =3D &bufs[idx]; buf->ops->release(pipe, buf); } pipe_unlock(pipe); =20 -out: kfree(bufs); return ret; } diff --git a/fs/pipe.c b/fs/pipe.c index 1e7263bb837a..6534470a6c19 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -178,9 +178,9 @@ EXPORT_SYMBOL(generic_pipe_buf_steal); * 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_buff= er *buf) +bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buff= er *buf) { - page_cache_get(buf->page); + return try_get_page(buf->page); } EXPORT_SYMBOL(generic_pipe_buf_get); =20 diff --git a/fs/splice.c b/fs/splice.c index fde126369966..57ccc583a172 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1876,7 +1876,11 @@ static int splice_pipe_to_pipe(struct pipe_inode_i= nfo *ipipe, * Get a reference to this pipe buffer, * so we can copy the contents over. */ - pipe_buf_get(ipipe, ibuf); + if (!pipe_buf_get(ipipe, ibuf)) { + if (ret =3D=3D 0) + ret =3D -EFAULT; + break; + } *obuf =3D *ibuf; =20 /* @@ -1948,7 +1952,11 @@ static int link_pipe(struct pipe_inode_info *ipipe= , * Get a reference to this pipe buffer, * so we can copy the contents over. */ - pipe_buf_get(ipipe, ibuf); + if (!pipe_buf_get(ipipe, ibuf)) { + if (ret =3D=3D 0) + ret =3D -EFAULT; + break; + } =20 obuf =3D opipe->bufs + nbuf; *obuf =3D *ibuf; diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 10876f3cb3da..0b28b65c12fb 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -112,18 +112,20 @@ struct pipe_buf_operations { /* * Get a reference to the pipe buffer. */ - void (*get)(struct pipe_inode_info *, struct pipe_buffer *); + bool (*get)(struct pipe_inode_info *, struct pipe_buffer *); }; =20 /** * pipe_buf_get - get a reference to a pipe_buffer * @pipe: the pipe that the buffer belongs to * @buf: the buffer to get a reference to + * + * Return: %true if the reference was successfully obtained. */ -static inline void pipe_buf_get(struct pipe_inode_info *pipe, +static inline __must_check bool pipe_buf_get(struct pipe_inode_info *pip= e, struct pipe_buffer *buf) { - buf->ops->get(pipe, buf); + return buf->ops->get(pipe, buf); } =20 /* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual @@ -148,7 +150,7 @@ struct pipe_inode_info *alloc_pipe_info(void); void free_pipe_info(struct pipe_inode_info *); =20 /* Generic pipe buffer ops functions */ -void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *= ); +bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *= ); int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffe= r *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer = *); void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buff= er *); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c6e4e3e7f685..32cc4ea93ad6 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5748,12 +5748,16 @@ static void buffer_pipe_buf_release(struct pipe_i= node_info *pipe, buf->private =3D 0; } =20 -static void buffer_pipe_buf_get(struct pipe_inode_info *pipe, +static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { struct buffer_ref *ref =3D (struct buffer_ref *)buf->private; =20 + if (ref->ref > INT_MAX/2) + return false; + ref->ref++; + return true; } =20 /* Pipe buffer operations for a buffer. */ --=20 2.23.0