• [PATCH v2 3/3] pipe: add proc_dopipe_max_size() to safely assign pipe_m

    From Joe Lawrence@21:1/5 to All on Mon Oct 2 22:50:03 2017
    pipe_max_size is assigned directly via procfs sysctl:

    static struct ctl_table fs_table[] = {
    ...
    {
    .procname = "pipe-max-size",
    .data = &pipe_max_size,
    .maxlen = sizeof(int),
    .mode = 0644,
    .proc_handler = &pipe_proc_fn,
    .extra1 = &pipe_min_size,
    },
    ...

    int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf,
    size_t *lenp, loff_t *ppos)
    {
    ...
    ret = proc_dointvec_minmax(table, write, buf, lenp, ppos)
    ...

    and then later rounded in-place a few statements later:

    ...
    pipe_max_size = round_pipe_size(pipe_max_size);
    ...

    This leaves a window of time between initial assignment and rounding
    that may be visible to other threads. (For example, one thread sets a non-rounded value to pipe_max_size while another reads its value.)

    Similar reads of pipe_max_size are potentially racey:

    pipe.c :: alloc_pipe_info()
    pipe.c :: pipe_set_size()

    Add a new proc_dopipe_max_size() function that consolidates reading the
    new value from the user buffer, verifying bounds, and calling
    round_pipe_size() with a single assignment to pipe_max_size.

    Reported-by: Mikulas Patocka <mpatocka@redhat.com>
    Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
    ---

    v2:

    - Fix !CONFIG_PROC_SYSCTL build case

    fs/pipe.c | 16 ++--------------
    include/linux/pipe_fs_i.h | 1 +
    include/linux/sysctl.h | 3 +++
    kernel/sysctl.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
    4 files changed, 55 insertions(+), 14 deletions(-)

    diff --git a/fs/pipe.c b/fs/pipe.c
    index 8cbc97d97753..4db3cd2d139c 100644
    --- a/fs/pipe.c
    +++ b/fs/pipe.c
    @@ -1019,7 +1019,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
    * Currently we rely on the pipe array holding a power-of-2 number
    * of pages. Returns 0 on error.
    */
    -static inline unsigned int round_pipe_size(unsigned int size)
    +unsigned int round_pipe_size(unsigned int size)
    {
    unsigned long nr_pages;

    @@ -1130,19 +1130,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
    int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf,
    size_t *lenp, loff_t *ppos)
    {
    - unsigned int rounded_pipe_max_size;
    - int ret;