Skip to content

Commit 9557090

Browse files
Matthew Wilcoxgregkh
authored andcommitted
fs: prevent page refcount overflow in pipe_buf_get
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 <[email protected]> Signed-off-by: Matthew Wilcox <[email protected]> Signed-off-by: Linus Torvalds <[email protected]> [bwh: Backported to 4.9: adjust context] Signed-off-by: Ben Hutchings <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6f3433c commit 9557090

File tree

5 files changed

+29
-15
lines changed

5 files changed

+29
-15
lines changed

fs/fuse/dev.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,10 +1975,8 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
19751975
rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len;
19761976

19771977
ret = -EINVAL;
1978-
if (rem < len) {
1979-
pipe_unlock(pipe);
1980-
goto out;
1981-
}
1978+
if (rem < len)
1979+
goto out_free;
19821980

19831981
rem = len;
19841982
while (rem) {
@@ -1996,7 +1994,9 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
19961994
pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
19971995
pipe->nrbufs--;
19981996
} else {
1999-
pipe_buf_get(pipe, ibuf);
1997+
if (!pipe_buf_get(pipe, ibuf))
1998+
goto out_free;
1999+
20002000
*obuf = *ibuf;
20012001
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
20022002
obuf->len = rem;
@@ -2019,11 +2019,11 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
20192019
ret = fuse_dev_do_write(fud, &cs, len);
20202020

20212021
pipe_lock(pipe);
2022+
out_free:
20222023
for (idx = 0; idx < nbuf; idx++)
20232024
pipe_buf_release(pipe, &bufs[idx]);
20242025
pipe_unlock(pipe);
20252026

2026-
out:
20272027
kfree(bufs);
20282028
return ret;
20292029
}

fs/pipe.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ EXPORT_SYMBOL(generic_pipe_buf_steal);
193193
* in the tee() system call, when we duplicate the buffers in one
194194
* pipe into another.
195195
*/
196-
void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
196+
bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
197197
{
198-
get_page(buf->page);
198+
return try_get_page(buf->page);
199199
}
200200
EXPORT_SYMBOL(generic_pipe_buf_get);
201201

fs/splice.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,7 +1585,11 @@ static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
15851585
* Get a reference to this pipe buffer,
15861586
* so we can copy the contents over.
15871587
*/
1588-
pipe_buf_get(ipipe, ibuf);
1588+
if (!pipe_buf_get(ipipe, ibuf)) {
1589+
if (ret == 0)
1590+
ret = -EFAULT;
1591+
break;
1592+
}
15891593
*obuf = *ibuf;
15901594

15911595
/*
@@ -1659,7 +1663,11 @@ static int link_pipe(struct pipe_inode_info *ipipe,
16591663
* Get a reference to this pipe buffer,
16601664
* so we can copy the contents over.
16611665
*/
1662-
pipe_buf_get(ipipe, ibuf);
1666+
if (!pipe_buf_get(ipipe, ibuf)) {
1667+
if (ret == 0)
1668+
ret = -EFAULT;
1669+
break;
1670+
}
16631671

16641672
obuf = opipe->bufs + nbuf;
16651673
*obuf = *ibuf;

include/linux/pipe_fs_i.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,20 @@ struct pipe_buf_operations {
107107
/*
108108
* Get a reference to the pipe buffer.
109109
*/
110-
void (*get)(struct pipe_inode_info *, struct pipe_buffer *);
110+
bool (*get)(struct pipe_inode_info *, struct pipe_buffer *);
111111
};
112112

113113
/**
114114
* pipe_buf_get - get a reference to a pipe_buffer
115115
* @pipe: the pipe that the buffer belongs to
116116
* @buf: the buffer to get a reference to
117+
*
118+
* Return: %true if the reference was successfully obtained.
117119
*/
118-
static inline void pipe_buf_get(struct pipe_inode_info *pipe,
120+
static inline __must_check bool pipe_buf_get(struct pipe_inode_info *pipe,
119121
struct pipe_buffer *buf)
120122
{
121-
buf->ops->get(pipe, buf);
123+
return buf->ops->get(pipe, buf);
122124
}
123125

124126
/**
@@ -178,7 +180,7 @@ struct pipe_inode_info *alloc_pipe_info(void);
178180
void free_pipe_info(struct pipe_inode_info *);
179181

180182
/* Generic pipe buffer ops functions */
181-
void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
183+
bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
182184
int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
183185
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
184186
void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);

kernel/trace/trace.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6145,12 +6145,16 @@ static void buffer_pipe_buf_release(struct pipe_inode_info *pipe,
61456145
buf->private = 0;
61466146
}
61476147

6148-
static void buffer_pipe_buf_get(struct pipe_inode_info *pipe,
6148+
static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe,
61496149
struct pipe_buffer *buf)
61506150
{
61516151
struct buffer_ref *ref = (struct buffer_ref *)buf->private;
61526152

6153+
if (ref->ref > INT_MAX/2)
6154+
return false;
6155+
61536156
ref->ref++;
6157+
return true;
61546158
}
61556159

61566160
/* Pipe buffer operations for a buffer. */

0 commit comments

Comments
 (0)