* [PATCH v2] Btrfs: fix btrfs_decompress_buf2page()
@ 2017-02-10 23:03 Omar Sandoval
2017-02-10 23:08 ` Omar Sandoval
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Omar Sandoval @ 2017-02-10 23:03 UTC (permalink / raw)
To: Chris Mason, linux-btrfs
Cc: Liu Bo, Christoph Hellwig, Pat Erley, kernel-team
From: Omar Sandoval <osandov@fb.com>
If btrfs_decompress_buf2page() is handed a bio with its page in the
middle of the working buffer, then we adjust the offset into the working
buffer. After we copy into the bio, we advance the iterator by the
number of bytes we copied. Then, we have some logic to handle the case
of discontiguous pages and adjust the offset into the working buffer
again. However, if we didn't advance the bio to a new page, we may enter
this case in error, essentially repeating the adjustment that we already
made when we entered the function. The end result is bogus data in the
bio.
Previously, we only checked for this case when we advanced to a new
page, but the conversion to bio iterators changed that. This restores
the old, correct behavior.
A case I saw when testing with zlib was:
buf_start = 42769
total_out = 46865
working_bytes = total_out - buf_start = 4096
start_byte = 45056
The condition (total_out > start_byte && buf_start < start_byte) is
true, so we adjust the offset:
buf_offset = start_byte - buf_start = 2287
working_bytes -= buf_offset = 1809
current_buf_start = buf_start = 42769
Then, we copy
bytes = min(bvec.bv_len, PAGE_SIZE - buf_offset, working_bytes) = 1809
buf_offset += bytes = 4096
working_bytes -= bytes = 0
current_buf_start += bytes = 44578
After bio_advance(), we are still in the same page, so start_byte is the
same. Then, we check (total_out > start_byte && current_buf_start < start_byte),
which is true! So, we adjust the values again:
buf_offset = start_byte - buf_start = 2287
working_bytes = total_out - start_byte = 1809
current_buf_start = buf_start + buf_offset = 45056
But note that working_bytes was already zero before this, so we should
have stopped copying.
Fixes: 974b1adc3b10 ("btrfs: use bio iterators for the decompression handlers")
Reported-by: Pat Erley <pat-lkml@erley.org>
Reviewed-by: Chris Mason <clm@fb.com>
Signed-off-by: Omar Sandoval <osandov@fb.com>
---
Changed from v1:
- Check if start_byte changed instead of whether bv_offset == 0. This is
a little more foolproof.
fs/btrfs/compression.c | 37 ++++++++++++++++++++-----------------
1 file changed, 20 insertions(+), 17 deletions(-)
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 7f390849343b..25168852ccde 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -1024,6 +1024,7 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start,
unsigned long buf_offset;
unsigned long current_buf_start;
unsigned long start_byte;
+ unsigned long prev_start_byte;
unsigned long working_bytes = total_out - buf_start;
unsigned long bytes;
char *kaddr;
@@ -1071,26 +1072,28 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start,
if (!bio->bi_iter.bi_size)
return 0;
bvec = bio_iter_iovec(bio, bio->bi_iter);
-
+ prev_start_byte = start_byte;
start_byte = page_offset(bvec.bv_page) - disk_start;
- /*
- * make sure our new page is covered by this
- * working buffer
- */
- if (total_out <= start_byte)
- return 1;
+ if (start_byte != prev_start_byte) {
+ /*
+ * make sure our new page is covered by this
+ * working buffer
+ */
+ if (total_out <= start_byte)
+ return 1;
- /*
- * the next page in the biovec might not be adjacent
- * to the last page, but it might still be found
- * inside this working buffer. bump our offset pointer
- */
- if (total_out > start_byte &&
- current_buf_start < start_byte) {
- buf_offset = start_byte - buf_start;
- working_bytes = total_out - start_byte;
- current_buf_start = buf_start + buf_offset;
+ /*
+ * the next page in the biovec might not be adjacent
+ * to the last page, but it might still be found
+ * inside this working buffer. bump our offset pointer
+ */
+ if (total_out > start_byte &&
+ current_buf_start < start_byte) {
+ buf_offset = start_byte - buf_start;
+ working_bytes = total_out - start_byte;
+ current_buf_start = buf_start + buf_offset;
+ }
}
}
--
2.11.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2] Btrfs: fix btrfs_decompress_buf2page()
2017-02-10 23:03 [PATCH v2] Btrfs: fix btrfs_decompress_buf2page() Omar Sandoval
@ 2017-02-10 23:08 ` Omar Sandoval
[not found] ` <1e52505b-b6fa-f60c-92d5-282ef25cbb9a@erley.org>
2017-02-15 3:32 ` Anand Jain
2 siblings, 0 replies; 5+ messages in thread
From: Omar Sandoval @ 2017-02-10 23:08 UTC (permalink / raw)
To: Chris Mason, linux-btrfs
Cc: Liu Bo, Christoph Hellwig, Pat Erley, kernel-team
On Fri, Feb 10, 2017 at 03:03:35PM -0800, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
>
> If btrfs_decompress_buf2page() is handed a bio with its page in the
> middle of the working buffer, then we adjust the offset into the working
> buffer. After we copy into the bio, we advance the iterator by the
> number of bytes we copied. Then, we have some logic to handle the case
> of discontiguous pages and adjust the offset into the working buffer
> again. However, if we didn't advance the bio to a new page, we may enter
> this case in error, essentially repeating the adjustment that we already
> made when we entered the function. The end result is bogus data in the
> bio.
>
> Previously, we only checked for this case when we advanced to a new
> page, but the conversion to bio iterators changed that. This restores
> the old, correct behavior.
>
> A case I saw when testing with zlib was:
>
> buf_start = 42769
> total_out = 46865
> working_bytes = total_out - buf_start = 4096
> start_byte = 45056
>
> The condition (total_out > start_byte && buf_start < start_byte) is
> true, so we adjust the offset:
>
> buf_offset = start_byte - buf_start = 2287
> working_bytes -= buf_offset = 1809
> current_buf_start = buf_start = 42769
>
> Then, we copy
>
> bytes = min(bvec.bv_len, PAGE_SIZE - buf_offset, working_bytes) = 1809
> buf_offset += bytes = 4096
> working_bytes -= bytes = 0
> current_buf_start += bytes = 44578
>
> After bio_advance(), we are still in the same page, so start_byte is the
> same. Then, we check (total_out > start_byte && current_buf_start < start_byte),
> which is true! So, we adjust the values again:
>
> buf_offset = start_byte - buf_start = 2287
> working_bytes = total_out - start_byte = 1809
> current_buf_start = buf_start + buf_offset = 45056
>
> But note that working_bytes was already zero before this, so we should
> have stopped copying.
>
> Fixes: 974b1adc3b10 ("btrfs: use bio iterators for the decompression handlers")
> Reported-by: Pat Erley <pat-lkml@erley.org>
> Reviewed-by: Chris Mason <clm@fb.com>
> Signed-off-by: Omar Sandoval <osandov@fb.com>
Whoops, I sent this out before I got your other email, Bo, but I assume
it's still okay for Chris to add your reviewed-by and tested-by?
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] Btrfs: fix btrfs_decompress_buf2page()
[not found] ` <1e52505b-b6fa-f60c-92d5-282ef25cbb9a@erley.org>
@ 2017-02-11 13:07 ` Chris Mason
0 siblings, 0 replies; 5+ messages in thread
From: Chris Mason @ 2017-02-11 13:07 UTC (permalink / raw)
To: Pat Erley
Cc: Omar Sandoval, linux-btrfs, Liu Bo, Christoph Hellwig,
kernel-team
On Fri, Feb 10, 2017 at 08:12:35PM -0800, Pat Erley wrote:
>
>
>On 02/10/17 15:03, Omar Sandoval wrote:
>>From: Omar Sandoval <osandov@fb.com>
>>
>>If btrfs_decompress_buf2page() is handed a bio with its page in the
>>middle of the working buffer, then we adjust the offset into the working
>>buffer. After we copy into the bio, we advance the iterator by the
>>number of bytes we copied. Then, we have some logic to handle the case
>>of discontiguous pages and adjust the offset into the working buffer
>>again. However, if we didn't advance the bio to a new page, we may enter
>>this case in error, essentially repeating the adjustment that we already
>>made when we entered the function. The end result is bogus data in the
>>bio.
>>
>>Previously, we only checked for this case when we advanced to a new
>>page, but the conversion to bio iterators changed that. This restores
>>the old, correct behavior.
>
>I can confirm this fixes the corruption I was seeing
>
>Feel free to add:
>
>Tested-by: Pat Erley <pat-lkml@erley.org>
Thanks again Pat for bisecting this down. It passed overnight so I'm
sending in right now.
-chris
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] Btrfs: fix btrfs_decompress_buf2page()
2017-02-10 23:03 [PATCH v2] Btrfs: fix btrfs_decompress_buf2page() Omar Sandoval
2017-02-10 23:08 ` Omar Sandoval
[not found] ` <1e52505b-b6fa-f60c-92d5-282ef25cbb9a@erley.org>
@ 2017-02-15 3:32 ` Anand Jain
2017-02-15 21:57 ` Omar Sandoval
2 siblings, 1 reply; 5+ messages in thread
From: Anand Jain @ 2017-02-15 3:32 UTC (permalink / raw)
To: Omar Sandoval, Chris Mason, linux-btrfs
Cc: Liu Bo, Christoph Hellwig, Pat Erley, kernel-team
On 02/11/17 07:03, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
>
> If btrfs_decompress_buf2page() is handed a bio with its page in the
> middle of the working buffer, then we adjust the offset into the working
> buffer. After we copy into the bio, we advance the iterator by the
> number of bytes we copied. Then, we have some logic to handle the case
> of discontiguous pages and adjust the offset into the working buffer
> again. However, if we didn't advance the bio to a new page, we may enter
> this case in error, essentially repeating the adjustment that we already
> made when we entered the function. The end result is bogus data in the
> bio.
>
> Previously, we only checked for this case when we advanced to a new
> page, but the conversion to bio iterators changed that. This restores
> the old, correct behavior.
>
> A case I saw when testing with zlib was:
>
> buf_start = 42769
> total_out = 46865
> working_bytes = total_out - buf_start = 4096
> start_byte = 45056
>
> The condition (total_out > start_byte && buf_start < start_byte) is
> true, so we adjust the offset:
>
> buf_offset = start_byte - buf_start = 2287
> working_bytes -= buf_offset = 1809
> current_buf_start = buf_start = 42769
>
> Then, we copy
>
> bytes = min(bvec.bv_len, PAGE_SIZE - buf_offset, working_bytes) = 1809
> buf_offset += bytes = 4096
> working_bytes -= bytes = 0
> current_buf_start += bytes = 44578
>
> After bio_advance(), we are still in the same page, so start_byte is the
> same. Then, we check (total_out > start_byte && current_buf_start < start_byte),
> which is true! So, we adjust the values again:
>
> buf_offset = start_byte - buf_start = 2287
> working_bytes = total_out - start_byte = 1809
> current_buf_start = buf_start + buf_offset = 45056
>
> But note that working_bytes was already zero before this, so we should
> have stopped copying.
Thanks Omar for nailing this down. How about a test case for this ?
-Anand
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] Btrfs: fix btrfs_decompress_buf2page()
2017-02-15 3:32 ` Anand Jain
@ 2017-02-15 21:57 ` Omar Sandoval
0 siblings, 0 replies; 5+ messages in thread
From: Omar Sandoval @ 2017-02-15 21:57 UTC (permalink / raw)
To: Anand Jain
Cc: Chris Mason, linux-btrfs, Liu Bo, Christoph Hellwig, Pat Erley,
kernel-team
On Wed, Feb 15, 2017 at 11:32:06AM +0800, Anand Jain wrote:
> Thanks Omar for nailing this down. How about a test case for this ?
Yeah, I'll put together a test case using your new compressible data
helper, thanks for adding that.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2017-02-15 21:58 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-02-10 23:03 [PATCH v2] Btrfs: fix btrfs_decompress_buf2page() Omar Sandoval
2017-02-10 23:08 ` Omar Sandoval
[not found] ` <1e52505b-b6fa-f60c-92d5-282ef25cbb9a@erley.org>
2017-02-11 13:07 ` Chris Mason
2017-02-15 3:32 ` Anand Jain
2017-02-15 21:57 ` Omar Sandoval
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).