aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorNick Piggin <nickpiggin@yahoo.com.au>2008-02-02 09:01:17 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-02-02 15:55:39 -0500
commit124d3b7041f9a0ca7c43a6293e1cae4576c32fd5 (patch)
tree9b92dd8f99c10ae0a0931ce71f3e9a20b32b167b /mm
parent6598b60fd56ba5e915a001cc4e307880a94d19ae (diff)
fix writev regression: pan hanging unkillable and un-straceable
Frederik Himpe reported an unkillable and un-straceable pan process. Zero length iovecs can go into an infinite loop in writev, because the iovec iterator does not always advance over them. The sequence required to trigger this is not trivial. I think it requires that a zero-length iovec be followed by a non-zero-length iovec which causes a pagefault in the atomic usercopy. This causes the writev code to drop back into single-segment copy mode, which then tries to copy the 0 bytes of the zero-length iovec; a zero length copy looks like a failure though, so it loops. Put a test into iov_iter_advance to catch zero-length iovecs. We could just put the test in the fallback path, but I feel it is more robust to skip over zero-length iovecs throughout the code (iovec iterator may be used in filesystems too, so it should be robust). Signed-off-by: Nick Piggin <npiggin@suse.de> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/filemap.c8
1 files changed, 6 insertions, 2 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index 89ce6fe5f8be..76bea88cbebc 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1750,7 +1750,11 @@ static void __iov_iter_advance_iov(struct iov_iter *i, size_t bytes)
1750 const struct iovec *iov = i->iov; 1750 const struct iovec *iov = i->iov;
1751 size_t base = i->iov_offset; 1751 size_t base = i->iov_offset;
1752 1752
1753 while (bytes) { 1753 /*
1754 * The !iov->iov_len check ensures we skip over unlikely
1755 * zero-length segments.
1756 */
1757 while (bytes || !iov->iov_len) {
1754 int copy = min(bytes, iov->iov_len - base); 1758 int copy = min(bytes, iov->iov_len - base);
1755 1759
1756 bytes -= copy; 1760 bytes -= copy;
@@ -2268,6 +2272,7 @@ again:
2268 2272
2269 cond_resched(); 2273 cond_resched();
2270 2274
2275 iov_iter_advance(i, copied);
2271 if (unlikely(copied == 0)) { 2276 if (unlikely(copied == 0)) {
2272 /* 2277 /*
2273 * If we were unable to copy any data at all, we must 2278 * If we were unable to copy any data at all, we must
@@ -2281,7 +2286,6 @@ again:
2281 iov_iter_single_seg_count(i)); 2286 iov_iter_single_seg_count(i));
2282 goto again; 2287 goto again;
2283 } 2288 }
2284 iov_iter_advance(i, copied);
2285 pos += copied; 2289 pos += copied;
2286 written += copied; 2290 written += copied;
2287 2291