aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2006-06-25 08:47:58 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-25 13:01:09 -0400
commit01408c4939479ec46c15aa7ef6e2406be50eeeca (patch)
tree106ee144cc7214cc5cb78bc35a49fc654ef16fe9 /fs
parent5f507d9e05b4dbfee34f3d967623ad3fbf0f28b3 (diff)
[PATCH] Prepare for __copy_from_user_inatomic to not zero missed bytes
The problem is that when we write to a file, the copy from userspace to pagecache is first done with preemption disabled, so if the source address is not immediately available the copy fails *and* *zeros* *the* *destination*. This is a problem because a concurrent read (which admittedly is an odd thing to do) might see zeros rather that was there before the write, or what was there after, or some mixture of the two (any of these being a reasonable thing to see). If the copy did fail, it will immediately be retried with preemption re-enabled so any transient problem with accessing the source won't cause an error. The first copying does not need to zero any uncopied bytes, and doing so causes the problem. It uses copy_from_user_atomic rather than copy_from_user so the simple expedient is to change copy_from_user_atomic to *not* zero out bytes on failure. The first of these two patches prepares for the change by fixing two places which assume copy_from_user_atomic does zero the tail. The two usages are very similar pieces of code which copy from a userspace iovec into one or more page-cache pages. These are changed to remove the assumption. The second patch changes __copy_from_user_inatomic* to not zero the tail. Once these are accepted, I will look at similar patches of other architectures where this is important (ppc, mips and sparc being the ones I can find). This patch: There is a problem with __copy_from_user_inatomic zeroing the tail of the buffer in the case of an error. As it is called in atomic context, the error may be transient, so it results in zeros being written where maybe they shouldn't be. In the usage in filemap, this opens a window for a well timed read to see data (zeros) which is not consistent with any ordering of reads and writes. Most cases where __copy_from_user_inatomic is called, a failure results in __copy_from_user being called immediately. As long as the latter zeros the tail, the former doesn't need to. However in *copy_from_user_iovec implementations (in both filemap and ntfs/file), it is assumed that copy_from_user_inatomic will zero the tail. This patch removes that assumption, so that after this patch it will be safe for copy_from_user_inatomic to not zero the tail. This patch also adds some commentary to filemap.h and asm-i386/uaccess.h. After this patch, all architectures that might disable preempt when kmap_atomic is called need to have their __copy_from_user_inatomic* "fixed". This includes - powerpc - i386 - mips - sparc Signed-off-by: Neil Brown <neilb@suse.de> Cc: David Howells <dhowells@redhat.com> Cc: Anton Altaparmakov <aia21@cantab.net> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: William Lee Irwin III <wli@holomorphy.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/ntfs/file.c26
1 files changed, 14 insertions, 12 deletions
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 88292f9e4b9b..2e42c2dcae12 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -1358,7 +1358,7 @@ err_out:
1358 goto out; 1358 goto out;
1359} 1359}
1360 1360
1361static size_t __ntfs_copy_from_user_iovec(char *vaddr, 1361static size_t __ntfs_copy_from_user_iovec_inatomic(char *vaddr,
1362 const struct iovec *iov, size_t iov_ofs, size_t bytes) 1362 const struct iovec *iov, size_t iov_ofs, size_t bytes)
1363{ 1363{
1364 size_t total = 0; 1364 size_t total = 0;
@@ -1376,10 +1376,6 @@ static size_t __ntfs_copy_from_user_iovec(char *vaddr,
1376 bytes -= len; 1376 bytes -= len;
1377 vaddr += len; 1377 vaddr += len;
1378 if (unlikely(left)) { 1378 if (unlikely(left)) {
1379 /*
1380 * Zero the rest of the target like __copy_from_user().
1381 */
1382 memset(vaddr, 0, bytes);
1383 total -= left; 1379 total -= left;
1384 break; 1380 break;
1385 } 1381 }
@@ -1420,11 +1416,13 @@ static inline void ntfs_set_next_iovec(const struct iovec **iovp,
1420 * pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s 1416 * pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s
1421 * single-segment behaviour. 1417 * single-segment behaviour.
1422 * 1418 *
1423 * We call the same helper (__ntfs_copy_from_user_iovec()) both when atomic and 1419 * We call the same helper (__ntfs_copy_from_user_iovec_inatomic()) both
1424 * when not atomic. This is ok because __ntfs_copy_from_user_iovec() calls 1420 * when atomic and when not atomic. This is ok because
1425 * __copy_from_user_inatomic() and it is ok to call this when non-atomic. In 1421 * __ntfs_copy_from_user_iovec_inatomic() calls __copy_from_user_inatomic()
1426 * fact, the only difference between __copy_from_user_inatomic() and 1422 * and it is ok to call this when non-atomic.
1427 * __copy_from_user() is that the latter calls might_sleep(). And on many 1423 * Infact, the only difference between __copy_from_user_inatomic() and
1424 * __copy_from_user() is that the latter calls might_sleep() and the former
1425 * should not zero the tail of the buffer on error. And on many
1428 * architectures __copy_from_user_inatomic() is just defined to 1426 * architectures __copy_from_user_inatomic() is just defined to
1429 * __copy_from_user() so it makes no difference at all on those architectures. 1427 * __copy_from_user() so it makes no difference at all on those architectures.
1430 */ 1428 */
@@ -1441,14 +1439,18 @@ static inline size_t ntfs_copy_from_user_iovec(struct page **pages,
1441 if (len > bytes) 1439 if (len > bytes)
1442 len = bytes; 1440 len = bytes;
1443 kaddr = kmap_atomic(*pages, KM_USER0); 1441 kaddr = kmap_atomic(*pages, KM_USER0);
1444 copied = __ntfs_copy_from_user_iovec(kaddr + ofs, 1442 copied = __ntfs_copy_from_user_iovec_inatomic(kaddr + ofs,
1445 *iov, *iov_ofs, len); 1443 *iov, *iov_ofs, len);
1446 kunmap_atomic(kaddr, KM_USER0); 1444 kunmap_atomic(kaddr, KM_USER0);
1447 if (unlikely(copied != len)) { 1445 if (unlikely(copied != len)) {
1448 /* Do it the slow way. */ 1446 /* Do it the slow way. */
1449 kaddr = kmap(*pages); 1447 kaddr = kmap(*pages);
1450 copied = __ntfs_copy_from_user_iovec(kaddr + ofs, 1448 copied = __ntfs_copy_from_user_iovec_inatomic(kaddr + ofs,
1451 *iov, *iov_ofs, len); 1449 *iov, *iov_ofs, len);
1450 /*
1451 * Zero the rest of the target like __copy_from_user().
1452 */
1453 memset(kaddr + ofs + copied, 0, len - copied);
1452 kunmap(*pages); 1454 kunmap(*pages);
1453 if (unlikely(copied != len)) 1455 if (unlikely(copied != len))
1454 goto err_out; 1456 goto err_out;