aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Piggin <nickpiggin@yahoo.com.au>2008-02-02 15:01:17 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2008-02-03 07:55:39 +1100
commit124d3b7041f9a0ca7c43a6293e1cae4576c32fd5 (patch)
tree9b92dd8f99c10ae0a0931ce71f3e9a20b32b167b
parent6598b60fd56ba5e915a001cc4e307880a94d19ae (diff)
downloadkernel_samsung_crespo-124d3b7041f9a0ca7c43a6293e1cae4576c32fd5.zip
kernel_samsung_crespo-124d3b7041f9a0ca7c43a6293e1cae4576c32fd5.tar.gz
kernel_samsung_crespo-124d3b7041f9a0ca7c43a6293e1cae4576c32fd5.tar.bz2
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>
-rw-r--r--mm/filemap.c8
1 files changed, 6 insertions, 2 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index 89ce6fe..76bea88 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)
const struct iovec *iov = i->iov;
size_t base = i->iov_offset;
- while (bytes) {
+ /*
+ * The !iov->iov_len check ensures we skip over unlikely
+ * zero-length segments.
+ */
+ while (bytes || !iov->iov_len) {
int copy = min(bytes, iov->iov_len - base);
bytes -= copy;
@@ -2268,6 +2272,7 @@ again:
cond_resched();
+ iov_iter_advance(i, copied);
if (unlikely(copied == 0)) {
/*
* If we were unable to copy any data at all, we must
@@ -2281,7 +2286,6 @@ again:
iov_iter_single_seg_count(i));
goto again;
}
- iov_iter_advance(i, copied);
pos += copied;
written += copied;