Message ID | 1408637375-11343-14-git-send-email-hch@lst.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, 21 Aug 2014 11:09:29 -0500 Christoph Hellwig <hch@lst.de> wrote: > When we do non-page sized reads we can underflow the extent_length variable > and read incorrect data. Fix the extent_length calculation and change to > defensive <= checks for the extent length in the read and write path. > > Signed-off-by: Christoph Hellwig <hch@lst.de> Hi Christoph, I was reviewing this patch for possible backport. As 'extent_length' is sector_t, it is unsigned (either u64 or unsigned long). So comparing "<= 0" has the same effect as comparing "== 0". So the new checks are not "defensive". That doesn't mean they are wrong, but they could be misleading... There may be nothing that needs to be done here, but I thought I should let you know. NeilBrown > --- > fs/nfs/blocklayout/blocklayout.c | 7 ++++--- > 1 file changed, 4 insertions(+), 3 deletions(-) > > diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c > index 5427ae7..87a633d 100644 > --- a/fs/nfs/blocklayout/blocklayout.c > +++ b/fs/nfs/blocklayout/blocklayout.c > @@ -272,7 +272,7 @@ bl_read_pagelist(struct nfs_pgio_header *hdr) > isect = (sector_t) (f_offset >> SECTOR_SHIFT); > /* Code assumes extents are page-aligned */ > for (i = pg_index; i < hdr->page_array.npages; i++) { > - if (!extent_length) { > + if (extent_length <= 0) { > /* We've used up the previous extent */ > bl_put_extent(be); > bl_put_extent(cow_read); > @@ -303,6 +303,7 @@ bl_read_pagelist(struct nfs_pgio_header *hdr) > f_offset += pg_len; > bytes_left -= pg_len; > isect += (pg_offset >> SECTOR_SHIFT); > + extent_length -= (pg_offset >> SECTOR_SHIFT); > } else { > pg_offset = 0; > pg_len = PAGE_CACHE_SIZE; > @@ -333,7 +334,7 @@ bl_read_pagelist(struct nfs_pgio_header *hdr) > } > } > isect += (pg_len >> SECTOR_SHIFT); > - extent_length -= PAGE_CACHE_SECTORS; > + extent_length -= (pg_len >> SECTOR_SHIFT); > } > if ((isect << SECTOR_SHIFT) >= header->inode->i_size) { > hdr->res.eof = 1; > @@ -797,7 +798,7 @@ next_page: > /* Middle pages */ > pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT; > for (i = pg_index; i < header->page_array.npages; i++) { > - if (!extent_length) { > + if (extent_length <= 0) { > /* We've used up the previous extent */ > bl_put_extent(be); > bl_put_extent(cow_read);
On Mon, Feb 09, 2015 at 05:01:40PM +1100, NeilBrown wrote: > Hi Christoph, > I was reviewing this patch for possible backport. > As 'extent_length' is sector_t, it is unsigned (either u64 or unsigned long). > > So comparing "<= 0" has the same effect as comparing "== 0". > So the new checks are not "defensive". > > That doesn't mean they are wrong, but they could be misleading... > > There may be nothing that needs to be done here, but I thought I should let > you know. I think this is just an artefact of my earlier unsuccessful attempts at solving the problem. The real fix is the part that calculates the "extent_length" variable correctly. -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 5427ae7..87a633d 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -272,7 +272,7 @@ bl_read_pagelist(struct nfs_pgio_header *hdr) isect = (sector_t) (f_offset >> SECTOR_SHIFT); /* Code assumes extents are page-aligned */ for (i = pg_index; i < hdr->page_array.npages; i++) { - if (!extent_length) { + if (extent_length <= 0) { /* We've used up the previous extent */ bl_put_extent(be); bl_put_extent(cow_read); @@ -303,6 +303,7 @@ bl_read_pagelist(struct nfs_pgio_header *hdr) f_offset += pg_len; bytes_left -= pg_len; isect += (pg_offset >> SECTOR_SHIFT); + extent_length -= (pg_offset >> SECTOR_SHIFT); } else { pg_offset = 0; pg_len = PAGE_CACHE_SIZE; @@ -333,7 +334,7 @@ bl_read_pagelist(struct nfs_pgio_header *hdr) } } isect += (pg_len >> SECTOR_SHIFT); - extent_length -= PAGE_CACHE_SECTORS; + extent_length -= (pg_len >> SECTOR_SHIFT); } if ((isect << SECTOR_SHIFT) >= header->inode->i_size) { hdr->res.eof = 1; @@ -797,7 +798,7 @@ next_page: /* Middle pages */ pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT; for (i = pg_index; i < header->page_array.npages; i++) { - if (!extent_length) { + if (extent_length <= 0) { /* We've used up the previous extent */ bl_put_extent(be); bl_put_extent(cow_read);
When we do non-page sized reads we can underflow the extent_length variable and read incorrect data. Fix the extent_length calculation and change to defensive <= checks for the extent length in the read and write path. Signed-off-by: Christoph Hellwig <hch@lst.de> --- fs/nfs/blocklayout/blocklayout.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)