diff options
| author | Eric Sandeen <sandeen@redhat.com> | 2018-12-21 11:42:50 -0500 |
|---|---|---|
| committer | Darrick J. Wong <darrick.wong@oracle.com> | 2018-12-21 11:42:50 -0500 |
| commit | 3cc31fa65d85610574c0f6a474e89f4c419923d5 (patch) | |
| tree | 879ff2132f5bb4fff6bcf39af0a77881e6ac1e4e | |
| parent | 7566ec393f4161572ba6f11ad5171fd5d59b0fbd (diff) | |
iomap: don't search past page end in iomap_is_partially_uptodate
iomap_is_partially_uptodate() is intended to check wither blocks within
the selected range of a not-uptodate page are uptodate; if the range we
care about is up to date, it's an optimization.
However, the iomap implementation continues to check all blocks up to
from+count, which is beyond the page, and can even be well beyond the
iop->uptodate bitmap.
I think the worst that will happen is that we may eventually find a zero
bit and return "not partially uptodate" when it would have otherwise
returned true, and skip the optimization. Still, it's clearly an invalid
memory access that must be fixed.
So: fix this by limiting the search to within the page as is done in the
non-iomap variant, block_is_partially_uptodate().
Zorro noticed thiswhen KASAN went off for 512 byte blocks on a 64k
page system:
BUG: KASAN: slab-out-of-bounds in iomap_is_partially_uptodate+0x1a0/0x1e0
Read of size 8 at addr ffff800120c3a318 by task fsstress/22337
Reported-by: Zorro Lang <zlang@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
| -rw-r--r-- | fs/iomap.c | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/fs/iomap.c b/fs/iomap.c index 5bc172f3dfe8..fba8fdb734e0 100644 --- a/fs/iomap.c +++ b/fs/iomap.c | |||
| @@ -499,16 +499,29 @@ done: | |||
| 499 | } | 499 | } |
| 500 | EXPORT_SYMBOL_GPL(iomap_readpages); | 500 | EXPORT_SYMBOL_GPL(iomap_readpages); |
| 501 | 501 | ||
| 502 | /* | ||
| 503 | * iomap_is_partially_uptodate checks whether blocks within a page are | ||
| 504 | * uptodate or not. | ||
| 505 | * | ||
| 506 | * Returns true if all blocks which correspond to a file portion | ||
| 507 | * we want to read within the page are uptodate. | ||
| 508 | */ | ||
| 502 | int | 509 | int |
| 503 | iomap_is_partially_uptodate(struct page *page, unsigned long from, | 510 | iomap_is_partially_uptodate(struct page *page, unsigned long from, |
| 504 | unsigned long count) | 511 | unsigned long count) |
| 505 | { | 512 | { |
| 506 | struct iomap_page *iop = to_iomap_page(page); | 513 | struct iomap_page *iop = to_iomap_page(page); |
| 507 | struct inode *inode = page->mapping->host; | 514 | struct inode *inode = page->mapping->host; |
| 508 | unsigned first = from >> inode->i_blkbits; | 515 | unsigned len, first, last; |
| 509 | unsigned last = (from + count - 1) >> inode->i_blkbits; | ||
| 510 | unsigned i; | 516 | unsigned i; |
| 511 | 517 | ||
| 518 | /* Limit range to one page */ | ||
| 519 | len = min_t(unsigned, PAGE_SIZE - from, count); | ||
| 520 | |||
| 521 | /* First and last blocks in range within page */ | ||
| 522 | first = from >> inode->i_blkbits; | ||
| 523 | last = (from + len - 1) >> inode->i_blkbits; | ||
| 524 | |||
| 512 | if (iop) { | 525 | if (iop) { |
| 513 | for (i = first; i <= last; i++) | 526 | for (i = first; i <= last; i++) |
| 514 | if (!test_bit(i, iop->uptodate)) | 527 | if (!test_bit(i, iop->uptodate)) |
