>From 466fcd213793238c0283f71252dd1652dcd21e10 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Fri, 12 Aug 2016 13:08:55 +0200 Subject: [PATCH] fs/pipe: simplify pipe size handling a bit Pipe sizes are converted back and forth between pages and bytes. To make things a bit simpler we can do most of it in pages only. The exception is /proc/sys/fs/pipe-max-size which must be in bytes for backwards compatibility. There are two related races in the original code when a value is written to /proc/sys/fs/pipe-max-size: 1) reading /proc/sys/fs/pipe-max-size can yield the non-adjusted value 2) F_SETPIPE_SZ compares the argument to the non-adjusted value These races are harmless, however, because although somebody can see a lower value than they should, the value is never lower than what was written to /proc/sys/fs/pipe-max-size in the first place. Nevertheless, the patch should fix race #2. Signed-off-by: Vegard Nossum --- fs/pipe.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 42ea89f..841f5bd 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -32,7 +32,9 @@ * The max size that a non-root user is allowed to grow the pipe. Can * be set by root in /proc/sys/fs/pipe-max-size */ -unsigned int pipe_max_size = 1048576; +#define PIPE_MAX_PAGES 256 +unsigned int pipe_max_pages = PIPE_MAX_PAGES; +unsigned int pipe_max_size = PIPE_MAX_PAGES << PAGE_SHIFT; /* * Minimum pipe size, as required by POSIX @@ -1065,12 +1067,12 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) * Currently we rely on the pipe array holding a power-of-2 number * of pages. */ -static inline unsigned int round_pipe_size(unsigned int size) +static inline unsigned int round_pipe_pages(unsigned int size) { unsigned long nr_pages; nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - return roundup_pow_of_two(nr_pages) << PAGE_SHIFT; + return roundup_pow_of_two(nr_pages); } /* @@ -1086,7 +1088,9 @@ int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf, if (ret < 0 || !write) return ret; - pipe_max_size = round_pipe_size(pipe_max_size); + /* Slightly racy; a reader may see the non-sounded pipe_max_size */ + pipe_max_pages = round_pipe_pages(pipe_max_size); + pipe_max_size = pipe_max_pages << PAGE_SHIFT; return ret; } @@ -1113,16 +1117,14 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case F_SETPIPE_SZ: { - unsigned int size, nr_pages; + unsigned int nr_pages; ret = -EINVAL; if (!arg || arg > INT_MAX) goto out; - size = round_pipe_size(arg); - nr_pages = size >> PAGE_SHIFT; - - if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { + nr_pages = round_pipe_pages(arg); + if (!capable(CAP_SYS_RESOURCE) && nr_pages > pipe_max_pages) { ret = -EPERM; goto out; } else if ((too_many_pipe_buffers_hard(pipe->user) || -- 1.9.1