From: Greg Kroah-Hartman Date: Wed, 11 Jul 2012 23:00:22 +0000 (-0700) Subject: 3.4-stable patches X-Git-Tag: v3.0.37~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5ea3e7dc977522c95ae942ceaa74da3807d21aec;p=thirdparty%2Fkernel%2Fstable-queue.git 3.4-stable patches added patches: splice-fix-racy-pipe-buffers-uses.patch --- diff --git a/queue-3.4/series b/queue-3.4/series index 8185da9d4a0..52190372c5c 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -171,3 +171,4 @@ thp-avoid-atomic64_read-in-pmd_read_atomic-for-32bit-pae.patch rtl8187-brightness_set-can-not-sleep.patch macvtap-zerocopy-validate-vectors-before-building-skb.patch net-wireless-ipw2x00-add-supported-cipher-suites-to-wiphy-initialization.patch +splice-fix-racy-pipe-buffers-uses.patch diff --git a/queue-3.4/splice-fix-racy-pipe-buffers-uses.patch b/queue-3.4/splice-fix-racy-pipe-buffers-uses.patch new file mode 100644 index 00000000000..0da23043e41 --- /dev/null +++ b/queue-3.4/splice-fix-racy-pipe-buffers-uses.patch @@ -0,0 +1,285 @@ +From: Eric Dumazet +Date: Tue, 12 Jun 2012 15:24:40 +0200 +Subject: splice: fix racy pipe->buffers uses + +From: Eric Dumazet + +commit 047fe3605235888f3ebcda0c728cb31937eadfe6 upstream. + +Dave Jones reported a kernel BUG at mm/slub.c:3474! triggered +by splice_shrink_spd() called from vmsplice_to_pipe() + +commit 35f3d14dbbc5 (pipe: add support for shrinking and growing pipes) +added capability to adjust pipe->buffers. + +Problem is some paths don't hold pipe mutex and assume pipe->buffers +doesn't change for their duration. + +Fix this by adding nr_pages_max field in struct splice_pipe_desc, and +use it in place of pipe->buffers where appropriate. + +splice_shrink_spd() loses its struct pipe_inode_info argument. + +Reported-by: Dave Jones +Signed-off-by: Eric Dumazet +Cc: Jens Axboe +Cc: Alexander Viro +Cc: Tom Herbert +Tested-by: Dave Jones +Signed-off-by: Jens Axboe +[bwh: Backported to 3.2: + - Adjust context in vmsplice_to_pipe() + - Update one more call to splice_shrink_spd(), from skb_splice_bits()] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + fs/splice.c | 35 ++++++++++++++++++++--------------- + include/linux/splice.h | 8 ++++---- + kernel/relay.c | 5 +++-- + kernel/trace/trace.c | 6 ++++-- + mm/shmem.c | 3 ++- + net/core/skbuff.c | 3 ++- + 6 files changed, 35 insertions(+), 25 deletions(-) + +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -273,13 +273,16 @@ void spd_release_page(struct splice_pipe + * Check if we need to grow the arrays holding pages and partial page + * descriptions. + */ +-int splice_grow_spd(struct pipe_inode_info *pipe, struct splice_pipe_desc *spd) ++int splice_grow_spd(const struct pipe_inode_info *pipe, struct splice_pipe_desc *spd) + { +- if (pipe->buffers <= PIPE_DEF_BUFFERS) ++ unsigned int buffers = ACCESS_ONCE(pipe->buffers); ++ ++ spd->nr_pages_max = buffers; ++ if (buffers <= PIPE_DEF_BUFFERS) + return 0; + +- spd->pages = kmalloc(pipe->buffers * sizeof(struct page *), GFP_KERNEL); +- spd->partial = kmalloc(pipe->buffers * sizeof(struct partial_page), GFP_KERNEL); ++ spd->pages = kmalloc(buffers * sizeof(struct page *), GFP_KERNEL); ++ spd->partial = kmalloc(buffers * sizeof(struct partial_page), GFP_KERNEL); + + if (spd->pages && spd->partial) + return 0; +@@ -289,10 +292,9 @@ int splice_grow_spd(struct pipe_inode_in + return -ENOMEM; + } + +-void splice_shrink_spd(struct pipe_inode_info *pipe, +- struct splice_pipe_desc *spd) ++void splice_shrink_spd(struct splice_pipe_desc *spd) + { +- if (pipe->buffers <= PIPE_DEF_BUFFERS) ++ if (spd->nr_pages_max <= PIPE_DEF_BUFFERS) + return; + + kfree(spd->pages); +@@ -315,6 +317,7 @@ __generic_file_splice_read(struct file * + struct splice_pipe_desc spd = { + .pages = pages, + .partial = partial, ++ .nr_pages_max = PIPE_DEF_BUFFERS, + .flags = flags, + .ops = &page_cache_pipe_buf_ops, + .spd_release = spd_release_page, +@@ -326,7 +329,7 @@ __generic_file_splice_read(struct file * + index = *ppos >> PAGE_CACHE_SHIFT; + loff = *ppos & ~PAGE_CACHE_MASK; + req_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; +- nr_pages = min(req_pages, pipe->buffers); ++ nr_pages = min(req_pages, spd.nr_pages_max); + + /* + * Lookup the (hopefully) full range of pages we need. +@@ -497,7 +500,7 @@ fill_it: + if (spd.nr_pages) + error = splice_to_pipe(pipe, &spd); + +- splice_shrink_spd(pipe, &spd); ++ splice_shrink_spd(&spd); + return error; + } + +@@ -598,6 +601,7 @@ ssize_t default_file_splice_read(struct + struct splice_pipe_desc spd = { + .pages = pages, + .partial = partial, ++ .nr_pages_max = PIPE_DEF_BUFFERS, + .flags = flags, + .ops = &default_pipe_buf_ops, + .spd_release = spd_release_page, +@@ -608,8 +612,8 @@ ssize_t default_file_splice_read(struct + + res = -ENOMEM; + vec = __vec; +- if (pipe->buffers > PIPE_DEF_BUFFERS) { +- vec = kmalloc(pipe->buffers * sizeof(struct iovec), GFP_KERNEL); ++ if (spd.nr_pages_max > PIPE_DEF_BUFFERS) { ++ vec = kmalloc(spd.nr_pages_max * sizeof(struct iovec), GFP_KERNEL); + if (!vec) + goto shrink_ret; + } +@@ -617,7 +621,7 @@ ssize_t default_file_splice_read(struct + offset = *ppos & ~PAGE_CACHE_MASK; + nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + +- for (i = 0; i < nr_pages && i < pipe->buffers && len; i++) { ++ for (i = 0; i < nr_pages && i < spd.nr_pages_max && len; i++) { + struct page *page; + + page = alloc_page(GFP_USER); +@@ -665,7 +669,7 @@ ssize_t default_file_splice_read(struct + shrink_ret: + if (vec != __vec) + kfree(vec); +- splice_shrink_spd(pipe, &spd); ++ splice_shrink_spd(&spd); + return res; + + err: +@@ -1612,6 +1616,7 @@ static long vmsplice_to_pipe(struct file + struct splice_pipe_desc spd = { + .pages = pages, + .partial = partial, ++ .nr_pages_max = PIPE_DEF_BUFFERS, + .flags = flags, + .ops = &user_page_pipe_buf_ops, + .spd_release = spd_release_page, +@@ -1627,13 +1632,13 @@ static long vmsplice_to_pipe(struct file + + spd.nr_pages = get_iovec_page_array(iov, nr_segs, spd.pages, + spd.partial, flags & SPLICE_F_GIFT, +- pipe->buffers); ++ spd.nr_pages_max); + if (spd.nr_pages <= 0) + ret = spd.nr_pages; + else + ret = splice_to_pipe(pipe, &spd); + +- splice_shrink_spd(pipe, &spd); ++ splice_shrink_spd(&spd); + return ret; + } + +--- a/include/linux/splice.h ++++ b/include/linux/splice.h +@@ -51,7 +51,8 @@ struct partial_page { + struct splice_pipe_desc { + struct page **pages; /* page map */ + struct partial_page *partial; /* pages[] may not be contig */ +- int nr_pages; /* number of pages in map */ ++ int nr_pages; /* number of populated pages in map */ ++ unsigned int nr_pages_max; /* pages[] & partial[] arrays size */ + unsigned int flags; /* splice flags */ + const struct pipe_buf_operations *ops;/* ops associated with output pipe */ + void (*spd_release)(struct splice_pipe_desc *, unsigned int); +@@ -85,9 +86,8 @@ extern ssize_t splice_direct_to_actor(st + /* + * for dynamic pipe sizing + */ +-extern int splice_grow_spd(struct pipe_inode_info *, struct splice_pipe_desc *); +-extern void splice_shrink_spd(struct pipe_inode_info *, +- struct splice_pipe_desc *); ++extern int splice_grow_spd(const struct pipe_inode_info *, struct splice_pipe_desc *); ++extern void splice_shrink_spd(struct splice_pipe_desc *); + extern void spd_release_page(struct splice_pipe_desc *, unsigned int); + + extern const struct pipe_buf_operations page_cache_pipe_buf_ops; +--- a/kernel/relay.c ++++ b/kernel/relay.c +@@ -1235,6 +1235,7 @@ static ssize_t subbuf_splice_actor(struc + struct splice_pipe_desc spd = { + .pages = pages, + .nr_pages = 0, ++ .nr_pages_max = PIPE_DEF_BUFFERS, + .partial = partial, + .flags = flags, + .ops = &relay_pipe_buf_ops, +@@ -1302,8 +1303,8 @@ static ssize_t subbuf_splice_actor(struc + ret += padding; + + out: +- splice_shrink_spd(pipe, &spd); +- return ret; ++ splice_shrink_spd(&spd); ++ return ret; + } + + static ssize_t relay_file_splice_read(struct file *in, +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -3565,6 +3565,7 @@ static ssize_t tracing_splice_read_pipe( + .pages = pages_def, + .partial = partial_def, + .nr_pages = 0, /* This gets updated below. */ ++ .nr_pages_max = PIPE_DEF_BUFFERS, + .flags = flags, + .ops = &tracing_pipe_buf_ops, + .spd_release = tracing_spd_release_pipe, +@@ -3636,7 +3637,7 @@ static ssize_t tracing_splice_read_pipe( + + ret = splice_to_pipe(pipe, &spd); + out: +- splice_shrink_spd(pipe, &spd); ++ splice_shrink_spd(&spd); + return ret; + + out_err: +@@ -4126,6 +4127,7 @@ tracing_buffers_splice_read(struct file + struct splice_pipe_desc spd = { + .pages = pages_def, + .partial = partial_def, ++ .nr_pages_max = PIPE_DEF_BUFFERS, + .flags = flags, + .ops = &buffer_pipe_buf_ops, + .spd_release = buffer_spd_release, +@@ -4213,7 +4215,7 @@ tracing_buffers_splice_read(struct file + } + + ret = splice_to_pipe(pipe, &spd); +- splice_shrink_spd(pipe, &spd); ++ splice_shrink_spd(&spd); + out: + return ret; + } +--- a/mm/shmem.c ++++ b/mm/shmem.c +@@ -1365,6 +1365,7 @@ static ssize_t shmem_file_splice_read(st + struct splice_pipe_desc spd = { + .pages = pages, + .partial = partial, ++ .nr_pages_max = PIPE_DEF_BUFFERS, + .flags = flags, + .ops = &page_cache_pipe_buf_ops, + .spd_release = spd_release_page, +@@ -1453,7 +1454,7 @@ static ssize_t shmem_file_splice_read(st + if (spd.nr_pages) + error = splice_to_pipe(pipe, &spd); + +- splice_shrink_spd(pipe, &spd); ++ splice_shrink_spd(&spd); + + if (error > 0) { + *ppos += error; +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -1712,6 +1712,7 @@ int skb_splice_bits(struct sk_buff *skb, + struct splice_pipe_desc spd = { + .pages = pages, + .partial = partial, ++ .nr_pages_max = MAX_SKB_FRAGS, + .flags = flags, + .ops = &sock_pipe_buf_ops, + .spd_release = sock_spd_release, +@@ -1758,7 +1759,7 @@ done: + lock_sock(sk); + } + +- splice_shrink_spd(pipe, &spd); ++ splice_shrink_spd(&spd); + return ret; + } +