Skip to content

Commit 15fab63

Browse files
Matthew Wilcoxtorvalds
authored andcommitted
fs: prevent page refcount overflow in pipe_buf_get
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]> Cc: [email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 8fde12c commit 15fab63

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
@@ -2034,10 +2034,8 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
20342034
rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len;
20352035

20362036
ret = -EINVAL;
2037-
if (rem < len) {
2038-
pipe_unlock(pipe);
2039-
goto out;
2040-
}
2037+
if (rem < len)
2038+
goto out_free;
20412039

20422040
rem = len;
20432041
while (rem) {
@@ -2055,7 +2053,9 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
20552053
pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
20562054
pipe->nrbufs--;
20572055
} else {
2058-
pipe_buf_get(pipe, ibuf);
2056+
if (!pipe_buf_get(pipe, ibuf))
2057+
goto out_free;
2058+
20592059
*obuf = *ibuf;
20602060
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
20612061
obuf->len = rem;
@@ -2078,11 +2078,11 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
20782078
ret = fuse_dev_do_write(fud, &cs, len);
20792079

20802080
pipe_lock(pipe);
2081+
out_free:
20812082
for (idx = 0; idx < nbuf; idx++)
20822083
pipe_buf_release(pipe, &bufs[idx]);
20832084
pipe_unlock(pipe);
20842085

2085-
out:
20862086
kvfree(bufs);
20872087
return ret;
20882088
}

fs/pipe.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ EXPORT_SYMBOL(generic_pipe_buf_steal);
189189
* in the tee() system call, when we duplicate the buffers in one
190190
* pipe into another.
191191
*/
192-
void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
192+
bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
193193
{
194-
get_page(buf->page);
194+
return try_get_page(buf->page);
195195
}
196196
EXPORT_SYMBOL(generic_pipe_buf_get);
197197

fs/splice.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,7 +1588,11 @@ static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
15881588
* Get a reference to this pipe buffer,
15891589
* so we can copy the contents over.
15901590
*/
1591-
pipe_buf_get(ipipe, ibuf);
1591+
if (!pipe_buf_get(ipipe, ibuf)) {
1592+
if (ret == 0)
1593+
ret = -EFAULT;
1594+
break;
1595+
}
15921596
*obuf = *ibuf;
15931597

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

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

include/linux/pipe_fs_i.h

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

114114
/**
115115
* pipe_buf_get - get a reference to a pipe_buffer
116116
* @pipe: the pipe that the buffer belongs to
117117
* @buf: the buffer to get a reference to
118+
*
119+
* Return: %true if the reference was successfully obtained.
118120
*/
119-
static inline void pipe_buf_get(struct pipe_inode_info *pipe,
121+
static inline __must_check bool pipe_buf_get(struct pipe_inode_info *pipe,
120122
struct pipe_buffer *buf)
121123
{
122-
buf->ops->get(pipe, buf);
124+
return buf->ops->get(pipe, buf);
123125
}
124126

125127
/**
@@ -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
@@ -6835,12 +6835,16 @@ static void buffer_pipe_buf_release(struct pipe_inode_info *pipe,
68356835
buf->private = 0;
68366836
}
68376837

6838-
static void buffer_pipe_buf_get(struct pipe_inode_info *pipe,
6838+
static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe,
68396839
struct pipe_buffer *buf)
68406840
{
68416841
struct buffer_ref *ref = (struct buffer_ref *)buf->private;
68426842

6843+
if (ref->ref > INT_MAX/2)
6844+
return false;
6845+
68436846
ref->ref++;
6847+
return true;
68446848
}
68456849

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

0 commit comments

Comments
 (0)