Skip to content

Commit

Permalink
Btrfs: Avoid accessing unmapped kernel address
Browse files Browse the repository at this point in the history
When decompressing a chunk of data, we'll copy the data out to
a working buffer if the data is stored in more than one page,
otherwise we'll use the mapped page directly to avoid memory
copy.

In the latter case, we'll end up accessing the kernel address
after we've unmapped the page in a corner case.

Reported-by: Juan Francisco Cantero Hurtado <iam@juanfra.info>
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
  • Loading branch information
Li Zefan authored and Chris Mason committed Feb 16, 2011
1 parent b4dc2b8 commit ca9b688
Showing 1 changed file with 14 additions and 7 deletions.
21 changes: 14 additions & 7 deletions fs/btrfs/lzo.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ static int lzo_decompress_biovec(struct list_head *ws,
unsigned long tot_out;
unsigned long tot_len;
char *buf;
bool may_late_unmap, need_unmap;

data_in = kmap(pages_in[0]);
tot_len = read_compress_length(data_in);
Expand All @@ -300,11 +301,13 @@ static int lzo_decompress_biovec(struct list_head *ws,

tot_in += in_len;
working_bytes = in_len;
may_late_unmap = need_unmap = false;

/* fast path: avoid using the working buffer */
if (in_page_bytes_left >= in_len) {
buf = data_in + in_offset;
bytes = in_len;
may_late_unmap = true;
goto cont;
}

Expand All @@ -329,14 +332,17 @@ static int lzo_decompress_biovec(struct list_head *ws,
if (working_bytes == 0 && tot_in >= tot_len)
break;

kunmap(pages_in[page_in_index]);
page_in_index++;
if (page_in_index >= total_pages_in) {
if (page_in_index + 1 >= total_pages_in) {
ret = -1;
data_in = NULL;
goto done;
}
data_in = kmap(pages_in[page_in_index]);

if (may_late_unmap)
need_unmap = true;
else
kunmap(pages_in[page_in_index]);

data_in = kmap(pages_in[++page_in_index]);

in_page_bytes_left = PAGE_CACHE_SIZE;
in_offset = 0;
Expand All @@ -346,6 +352,8 @@ static int lzo_decompress_biovec(struct list_head *ws,
out_len = lzo1x_worst_compress(PAGE_CACHE_SIZE);
ret = lzo1x_decompress_safe(buf, in_len, workspace->buf,
&out_len);
if (need_unmap)
kunmap(pages_in[page_in_index - 1]);
if (ret != LZO_E_OK) {
printk(KERN_WARNING "btrfs decompress failed\n");
ret = -1;
Expand All @@ -363,8 +371,7 @@ static int lzo_decompress_biovec(struct list_head *ws,
break;
}
done:
if (data_in)
kunmap(pages_in[page_in_index]);
kunmap(pages_in[page_in_index]);
return ret;
}

Expand Down

0 comments on commit ca9b688

Please sign in to comment.