fix 32-bit breakage in block device read(2)
PATCH 1/1 fix 32-bit breakage in block device read(2)
Here's the patch that I mentioned on today's concall. The patch isn't in 3.16-rc2, but it's needed if you want to run xfstests on 32-bit kernels on 3.16-rc1 or 3.16-rc2 without seeing massive failures.
- Ted
Quoted 1 line
From
71c73fb0fc61fa569d961b8f5ba21bf5597fc3f1Mon Sep 17 00:00:00 2001
From: Al Viro <[email protected]>
Date: Mon, 23 Jun 2014 11:31:36 -0400
Subject: [PATCH] [regression] fix 32-bit breakage in block device read(2)
blkdev_read_iter() wants to cap the iov_iter by the amount of data remaining to the end of device. That's what iov_iter_truncate() is for (trim iter->count if it's above the given limit). So far, so good, but the argument of iov_iter_truncate() is size_t, so on 32bit boxen (in case of a large device) we end up with that upper limit truncated down to 32 bits *before* comparing it with iter->count.
Easily fixed by making iov_iter_truncate() take 64bit argument - it does the right thing after such change (we only reach the assignment in there when the current value of iter->count is greater than the limit, i.e. for anything that would get truncated we don't reach the assignment at all) and that argument is not the new value of iter->count - it's an upper limit for such.
The overhead of passing u64 is not an issue - the thing is inlined, so callers passing size_t won't pay any penalty.
--- include/linux/uio.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
include/linux/uio.h +13 -1
@@ -94,8 +94,20 @@ static inline size_t iov_iter_count(struct iov_iter *i)return i->count; } -static inline void iov_iter_truncate (struct iov_iter *i,size_t count) +/* +* Cap the iov_iter by given limit; note that the second argument is +* *not* the new size - it's upper limit for such. Passing it a value +* greater than the amount of data in iov_iter is fine - it'll just do +* nothing in that case. +*/ +static inline void iov_iter_truncate (struct iov_iter *i,u64 count) { +/* +* count doesn't have to fit in size_t - comparison extends both +* operands to u64 here and any value that would be truncated by +* conversion in assignement is by definition greater than all +* values of size_t, including old i->count. +*/ if (i->count > count) i->count = count; }
Signature · 1 line
2.0.0