2005-04-17 02:20:36 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
2007-04-25 17:16:47 +04:00
* Copyright © 2001 - 2007 Red Hat , Inc .
2005-04-17 02:20:36 +04:00
*
* Created by David Woodhouse < dwmw2 @ infradead . org >
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
*/
2012-02-16 03:56:45 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/crc32.h>
# include <linux/pagemap.h>
# include <linux/mtd/mtd.h>
# include <linux/compiler.h>
# include "nodelist.h"
# include "compr.h"
int jffs2_read_dnode ( struct jffs2_sb_info * c , struct jffs2_inode_info * f ,
struct jffs2_full_dnode * fd , unsigned char * buf ,
int ofs , int len )
{
struct jffs2_raw_inode * ri ;
size_t readlen ;
uint32_t crc ;
unsigned char * decomprbuf = NULL ;
unsigned char * readbuf = NULL ;
int ret = 0 ;
ri = jffs2_alloc_raw_inode ( ) ;
if ( ! ri )
return - ENOMEM ;
ret = jffs2_flash_read ( c , ref_offset ( fd - > raw ) , sizeof ( * ri ) , & readlen , ( char * ) ri ) ;
if ( ret ) {
jffs2_free_raw_inode ( ri ) ;
2012-02-16 03:56:44 +04:00
pr_warn ( " Error reading node from 0x%08x: %d \n " ,
ref_offset ( fd - > raw ) , ret ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
if ( readlen ! = sizeof ( * ri ) ) {
jffs2_free_raw_inode ( ri ) ;
2012-02-16 03:56:44 +04:00
pr_warn ( " Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx \n " ,
ref_offset ( fd - > raw ) , sizeof ( * ri ) , readlen ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
crc = crc32 ( 0 , ri , sizeof ( * ri ) - 8 ) ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p \n " ,
2005-04-17 02:20:36 +04:00
ref_offset ( fd - > raw ) , je32_to_cpu ( ri - > node_crc ) ,
crc , je32_to_cpu ( ri - > dsize ) , je32_to_cpu ( ri - > csize ) ,
2012-02-16 03:56:43 +04:00
je32_to_cpu ( ri - > offset ) , buf ) ;
2005-04-17 02:20:36 +04:00
if ( crc ! = je32_to_cpu ( ri - > node_crc ) ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Node CRC %08x != calculated CRC %08x for node at %08x \n " ,
je32_to_cpu ( ri - > node_crc ) , crc , ref_offset ( fd - > raw ) ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
goto out_ri ;
}
/* There was a bug where we wrote hole nodes out with csize/dsize
swapped . Deal with it */
2005-11-07 14:16:07 +03:00
if ( ri - > compr = = JFFS2_COMPR_ZERO & & ! je32_to_cpu ( ri - > dsize ) & &
2005-04-17 02:20:36 +04:00
je32_to_cpu ( ri - > csize ) ) {
ri - > dsize = ri - > csize ;
ri - > csize = cpu_to_je32 ( 0 ) ;
}
D1 ( if ( ofs + len > je32_to_cpu ( ri - > dsize ) ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " jffs2_read_dnode() asked for %d bytes at %d from %d-byte node \n " ,
len , ofs , je32_to_cpu ( ri - > dsize ) ) ;
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
goto out_ri ;
} ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( ri - > compr = = JFFS2_COMPR_ZERO ) {
memset ( buf , 0 , len ) ;
goto out_ri ;
}
/* Cases:
Reading whole node and it ' s uncompressed - read directly to buffer provided , check CRC .
2005-11-07 14:16:07 +03:00
Reading whole node and it ' s compressed - read into comprbuf , check CRC and decompress to buffer provided
Reading partial node and it ' s uncompressed - read into readbuf , check CRC , and copy
2005-04-17 02:20:36 +04:00
Reading partial node and it ' s compressed - read into readbuf , check checksum , decompress to decomprbuf and copy
*/
if ( ri - > compr = = JFFS2_COMPR_NONE & & len = = je32_to_cpu ( ri - > dsize ) ) {
readbuf = buf ;
} else {
readbuf = kmalloc ( je32_to_cpu ( ri - > csize ) , GFP_KERNEL ) ;
if ( ! readbuf ) {
ret = - ENOMEM ;
goto out_ri ;
}
}
if ( ri - > compr ! = JFFS2_COMPR_NONE ) {
if ( len < je32_to_cpu ( ri - > dsize ) ) {
decomprbuf = kmalloc ( je32_to_cpu ( ri - > dsize ) , GFP_KERNEL ) ;
if ( ! decomprbuf ) {
ret = - ENOMEM ;
goto out_readbuf ;
}
} else {
decomprbuf = buf ;
}
} else {
decomprbuf = readbuf ;
}
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 2 , " Read %d bytes to %p \n " , je32_to_cpu ( ri - > csize ) ,
readbuf ) ;
2005-04-17 02:20:36 +04:00
ret = jffs2_flash_read ( c , ( ref_offset ( fd - > raw ) ) + sizeof ( * ri ) ,
je32_to_cpu ( ri - > csize ) , & readlen , readbuf ) ;
if ( ! ret & & readlen ! = je32_to_cpu ( ri - > csize ) )
ret = - EIO ;
if ( ret )
goto out_decomprbuf ;
crc = crc32 ( 0 , readbuf , je32_to_cpu ( ri - > csize ) ) ;
if ( crc ! = je32_to_cpu ( ri - > data_crc ) ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Data CRC %08x != calculated CRC %08x for node at %08x \n " ,
je32_to_cpu ( ri - > data_crc ) , crc , ref_offset ( fd - > raw ) ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
goto out_decomprbuf ;
}
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 2 , " Data CRC matches calculated CRC %08x \n " , crc ) ;
2005-04-17 02:20:36 +04:00
if ( ri - > compr ! = JFFS2_COMPR_NONE ) {
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 2 , " Decompress %d bytes from %p to %d bytes at %p \n " ,
je32_to_cpu ( ri - > csize ) , readbuf ,
je32_to_cpu ( ri - > dsize ) , decomprbuf ) ;
2005-04-17 02:20:36 +04:00
ret = jffs2_decompress ( c , f , ri - > compr | ( ri - > usercompr < < 8 ) , readbuf , decomprbuf , je32_to_cpu ( ri - > csize ) , je32_to_cpu ( ri - > dsize ) ) ;
if ( ret ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Error: jffs2_decompress returned %d \n " , ret ) ;
2005-04-17 02:20:36 +04:00
goto out_decomprbuf ;
}
}
if ( len < je32_to_cpu ( ri - > dsize ) ) {
memcpy ( buf , decomprbuf + ofs , len ) ;
}
out_decomprbuf :
if ( decomprbuf ! = buf & & decomprbuf ! = readbuf )
kfree ( decomprbuf ) ;
out_readbuf :
if ( readbuf ! = buf )
kfree ( readbuf ) ;
out_ri :
jffs2_free_raw_inode ( ri ) ;
return ret ;
}
int jffs2_read_inode_range ( struct jffs2_sb_info * c , struct jffs2_inode_info * f ,
unsigned char * buf , uint32_t offset , uint32_t len )
{
uint32_t end = offset + len ;
struct jffs2_node_frag * frag ;
int ret ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " %s(): ino #%u, range 0x%08x-0x%08x \n " ,
__func__ , f - > inocache - > ino , offset , offset + len ) ;
2005-04-17 02:20:36 +04:00
frag = jffs2_lookup_node_frag ( & f - > fragtree , offset ) ;
/* XXX FIXME: Where a single physical node actually shows up in two
frags , we read it twice . Don ' t do that . */
jffs2: Fix memory corruption in jffs2_read_inode_range()
In 2.6.23 kernel, commit a32ea1e1f925399e0d81ca3f7394a44a6dafa12c
("Fix read/truncate race") fixed a race in the generic code, and as a
side effect, now do_generic_file_read() can ask us to readpage() past
the i_size. This seems to be correctly handled by the block routines
(e.g. block_read_full_page() fills the page with zeroes in case if
somebody is trying to read past the last inode's block).
JFFS2 doesn't handle this; it assumes that it won't be asked to read
pages which don't exist -- and thus that there will be at least _one_
valid 'frag' on the page it's being asked to read. It will fill any
holes with the following memset:
memset(buf, 0, min(end, frag->ofs + frag->size) - offset);
When the 'closest smaller match' returned by jffs2_lookup_node_frag() is
actually on a previous page and ends before 'offset', that results in:
memset(buf, 0, <huge unsigned negative>);
Hopefully, in most cases the corruption is fatal, and quickly causing
random oopses, like this:
root@10.0.0.4:~/ltp-fs-20090531# ./testcases/kernel/fs/ftest/ftest01
Unable to handle kernel paging request for data at address 0x00000008
Faulting instruction address: 0xc01cd980
Oops: Kernel access of bad area, sig: 11 [#1]
[...]
NIP [c01cd980] rb_insert_color+0x38/0x184
LR [c0043978] enqueue_hrtimer+0x88/0xc4
Call Trace:
[c6c63b60] [c004f9a8] tick_sched_timer+0xa0/0xe4 (unreliable)
[c6c63b80] [c0043978] enqueue_hrtimer+0x88/0xc4
[c6c63b90] [c0043a48] __run_hrtimer+0x94/0xbc
[c6c63bb0] [c0044628] hrtimer_interrupt+0x140/0x2b8
[c6c63c10] [c000f8e8] timer_interrupt+0x13c/0x254
[c6c63c30] [c001352c] ret_from_except+0x0/0x14
--- Exception: 901 at memset+0x38/0x5c
LR = jffs2_read_inode_range+0x144/0x17c
[c6c63cf0] [00000000] (null) (unreliable)
This patch fixes the issue, plus fixes all LTP tests on NAND/UBI with
JFFS2 filesystem that were failing since 2.6.23 (seems like the bug
above also broke the truncation).
Reported-By: Anton Vorontsov <avorontsov@ru.mvista.com>
Tested-By: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-11-30 12:06:40 +03:00
/* Now we're pointing at the first frag which overlaps our page
* ( or perhaps is before it , if we ' ve been asked to read off the
* end of the file ) . */
2005-04-17 02:20:36 +04:00
while ( offset < end ) {
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 2 , " %s(): offset %d, end %d \n " ,
__func__ , offset , end ) ;
jffs2: Fix memory corruption in jffs2_read_inode_range()
In 2.6.23 kernel, commit a32ea1e1f925399e0d81ca3f7394a44a6dafa12c
("Fix read/truncate race") fixed a race in the generic code, and as a
side effect, now do_generic_file_read() can ask us to readpage() past
the i_size. This seems to be correctly handled by the block routines
(e.g. block_read_full_page() fills the page with zeroes in case if
somebody is trying to read past the last inode's block).
JFFS2 doesn't handle this; it assumes that it won't be asked to read
pages which don't exist -- and thus that there will be at least _one_
valid 'frag' on the page it's being asked to read. It will fill any
holes with the following memset:
memset(buf, 0, min(end, frag->ofs + frag->size) - offset);
When the 'closest smaller match' returned by jffs2_lookup_node_frag() is
actually on a previous page and ends before 'offset', that results in:
memset(buf, 0, <huge unsigned negative>);
Hopefully, in most cases the corruption is fatal, and quickly causing
random oopses, like this:
root@10.0.0.4:~/ltp-fs-20090531# ./testcases/kernel/fs/ftest/ftest01
Unable to handle kernel paging request for data at address 0x00000008
Faulting instruction address: 0xc01cd980
Oops: Kernel access of bad area, sig: 11 [#1]
[...]
NIP [c01cd980] rb_insert_color+0x38/0x184
LR [c0043978] enqueue_hrtimer+0x88/0xc4
Call Trace:
[c6c63b60] [c004f9a8] tick_sched_timer+0xa0/0xe4 (unreliable)
[c6c63b80] [c0043978] enqueue_hrtimer+0x88/0xc4
[c6c63b90] [c0043a48] __run_hrtimer+0x94/0xbc
[c6c63bb0] [c0044628] hrtimer_interrupt+0x140/0x2b8
[c6c63c10] [c000f8e8] timer_interrupt+0x13c/0x254
[c6c63c30] [c001352c] ret_from_except+0x0/0x14
--- Exception: 901 at memset+0x38/0x5c
LR = jffs2_read_inode_range+0x144/0x17c
[c6c63cf0] [00000000] (null) (unreliable)
This patch fixes the issue, plus fixes all LTP tests on NAND/UBI with
JFFS2 filesystem that were failing since 2.6.23 (seems like the bug
above also broke the truncation).
Reported-By: Anton Vorontsov <avorontsov@ru.mvista.com>
Tested-By: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-11-30 12:06:40 +03:00
if ( unlikely ( ! frag | | frag - > ofs > offset | |
frag - > ofs + frag - > size < = offset ) ) {
2005-04-17 02:20:36 +04:00
uint32_t holesize = end - offset ;
jffs2: Fix memory corruption in jffs2_read_inode_range()
In 2.6.23 kernel, commit a32ea1e1f925399e0d81ca3f7394a44a6dafa12c
("Fix read/truncate race") fixed a race in the generic code, and as a
side effect, now do_generic_file_read() can ask us to readpage() past
the i_size. This seems to be correctly handled by the block routines
(e.g. block_read_full_page() fills the page with zeroes in case if
somebody is trying to read past the last inode's block).
JFFS2 doesn't handle this; it assumes that it won't be asked to read
pages which don't exist -- and thus that there will be at least _one_
valid 'frag' on the page it's being asked to read. It will fill any
holes with the following memset:
memset(buf, 0, min(end, frag->ofs + frag->size) - offset);
When the 'closest smaller match' returned by jffs2_lookup_node_frag() is
actually on a previous page and ends before 'offset', that results in:
memset(buf, 0, <huge unsigned negative>);
Hopefully, in most cases the corruption is fatal, and quickly causing
random oopses, like this:
root@10.0.0.4:~/ltp-fs-20090531# ./testcases/kernel/fs/ftest/ftest01
Unable to handle kernel paging request for data at address 0x00000008
Faulting instruction address: 0xc01cd980
Oops: Kernel access of bad area, sig: 11 [#1]
[...]
NIP [c01cd980] rb_insert_color+0x38/0x184
LR [c0043978] enqueue_hrtimer+0x88/0xc4
Call Trace:
[c6c63b60] [c004f9a8] tick_sched_timer+0xa0/0xe4 (unreliable)
[c6c63b80] [c0043978] enqueue_hrtimer+0x88/0xc4
[c6c63b90] [c0043a48] __run_hrtimer+0x94/0xbc
[c6c63bb0] [c0044628] hrtimer_interrupt+0x140/0x2b8
[c6c63c10] [c000f8e8] timer_interrupt+0x13c/0x254
[c6c63c30] [c001352c] ret_from_except+0x0/0x14
--- Exception: 901 at memset+0x38/0x5c
LR = jffs2_read_inode_range+0x144/0x17c
[c6c63cf0] [00000000] (null) (unreliable)
This patch fixes the issue, plus fixes all LTP tests on NAND/UBI with
JFFS2 filesystem that were failing since 2.6.23 (seems like the bug
above also broke the truncation).
Reported-By: Anton Vorontsov <avorontsov@ru.mvista.com>
Tested-By: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-11-30 12:06:40 +03:00
if ( frag & & frag - > ofs > offset ) {
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x \n " ,
f - > inocache - > ino , frag - > ofs , offset ) ;
2005-04-17 02:20:36 +04:00
holesize = min ( holesize , frag - > ofs - offset ) ;
}
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Filling non-frag hole from %d-%d \n " ,
offset , offset + holesize ) ;
2005-04-17 02:20:36 +04:00
memset ( buf , 0 , holesize ) ;
buf + = holesize ;
offset + = holesize ;
continue ;
} else if ( unlikely ( ! frag - > node ) ) {
uint32_t holeend = min ( end , frag - > ofs + frag - > size ) ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Filling frag hole from %d-%d (frag 0x%x 0x%x) \n " ,
offset , holeend , frag - > ofs ,
frag - > ofs + frag - > size ) ;
2005-04-17 02:20:36 +04:00
memset ( buf , 0 , holeend - offset ) ;
buf + = holeend - offset ;
offset = holeend ;
frag = frag_next ( frag ) ;
continue ;
} else {
uint32_t readlen ;
uint32_t fragofs ; /* offset within the frag to start reading */
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
fragofs = offset - frag - > ofs ;
readlen = min ( frag - > size - fragofs , end - offset ) ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Reading %d-%d from node at 0x%08x (%d) \n " ,
frag - > ofs + fragofs ,
frag - > ofs + fragofs + readlen ,
ref_offset ( frag - > node - > raw ) ,
ref_flags ( frag - > node - > raw ) ) ;
2005-04-17 02:20:36 +04:00
ret = jffs2_read_dnode ( c , f , frag - > node , buf , fragofs + frag - > ofs - frag - > node - > ofs , readlen ) ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 2 , " node read done \n " ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " %s(): error %d \n " ,
__func__ , ret ) ;
2005-04-17 02:20:36 +04:00
memset ( buf , 0 , readlen ) ;
return ret ;
}
buf + = readlen ;
offset + = readlen ;
frag = frag_next ( frag ) ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 2 , " node read was OK. Looping \n " ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}