diff options
author | Theodore Ts'o <tytso@mit.edu> | 2010-10-16 19:36:59 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2011-01-06 05:52:15 -0500 |
commit | f0cad89f5e8ef8b6d0c065115565524137e44f0b (patch) | |
tree | 8eea669cb0317a2894edff505b132c5d3eb6fcfb /fs/ext3/namei.c | |
parent | ad692bf3ea035fa5a9d56462cf3df97d9607cced (diff) |
ext3: Avoid uninitialized memory references with a corrupted htree directory
If the first htree directory is missing '.' or '..' but is otherwise a
valid directory, and we do a lookup for '.' or '..', it's possible to
dereference an uninitialized memory pointer in ext3_htree_next_block().
Avoid this.
We avoid this by moving the special case from ext3_dx_find_entry() to
ext3_find_entry(); this also means we can optimize ext3_find_entry()
slightly when NFS looks up "..".
Thanks to Brad Spengler for pointing a Clang warning that led me to
look more closely at this code. The warning was harmless, but it was
useful in pointing out code that was too ugly to live. This warning was
also reported by Roman Borisov.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: Jan Kara <jack@suse.cz>
Cc: Brad Spengler <spender@grsecurity.net>
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/ext3/namei.c')
-rw-r--r-- | fs/ext3/namei.c | 30 |
1 files changed, 15 insertions, 15 deletions
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 672cea16a8b9..d093cbbe38b7 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c | |||
@@ -858,6 +858,7 @@ static struct buffer_head *ext3_find_entry(struct inode *dir, | |||
858 | struct buffer_head * bh_use[NAMEI_RA_SIZE]; | 858 | struct buffer_head * bh_use[NAMEI_RA_SIZE]; |
859 | struct buffer_head * bh, *ret = NULL; | 859 | struct buffer_head * bh, *ret = NULL; |
860 | unsigned long start, block, b; | 860 | unsigned long start, block, b; |
861 | const u8 *name = entry->name; | ||
861 | int ra_max = 0; /* Number of bh's in the readahead | 862 | int ra_max = 0; /* Number of bh's in the readahead |
862 | buffer, bh_use[] */ | 863 | buffer, bh_use[] */ |
863 | int ra_ptr = 0; /* Current index into readahead | 864 | int ra_ptr = 0; /* Current index into readahead |
@@ -871,6 +872,16 @@ static struct buffer_head *ext3_find_entry(struct inode *dir, | |||
871 | namelen = entry->len; | 872 | namelen = entry->len; |
872 | if (namelen > EXT3_NAME_LEN) | 873 | if (namelen > EXT3_NAME_LEN) |
873 | return NULL; | 874 | return NULL; |
875 | if ((namelen <= 2) && (name[0] == '.') && | ||
876 | (name[1] == '.' || name[1] == 0)) { | ||
877 | /* | ||
878 | * "." or ".." will only be in the first block | ||
879 | * NFS may look up ".."; "." should be handled by the VFS | ||
880 | */ | ||
881 | block = start = 0; | ||
882 | nblocks = 1; | ||
883 | goto restart; | ||
884 | } | ||
874 | if (is_dx(dir)) { | 885 | if (is_dx(dir)) { |
875 | bh = ext3_dx_find_entry(dir, entry, res_dir, &err); | 886 | bh = ext3_dx_find_entry(dir, entry, res_dir, &err); |
876 | /* | 887 | /* |
@@ -961,9 +972,8 @@ static struct buffer_head * ext3_dx_find_entry(struct inode *dir, | |||
961 | struct qstr *entry, struct ext3_dir_entry_2 **res_dir, | 972 | struct qstr *entry, struct ext3_dir_entry_2 **res_dir, |
962 | int *err) | 973 | int *err) |
963 | { | 974 | { |
964 | struct super_block * sb; | 975 | struct super_block *sb = dir->i_sb; |
965 | struct dx_hash_info hinfo; | 976 | struct dx_hash_info hinfo; |
966 | u32 hash; | ||
967 | struct dx_frame frames[2], *frame; | 977 | struct dx_frame frames[2], *frame; |
968 | struct ext3_dir_entry_2 *de, *top; | 978 | struct ext3_dir_entry_2 *de, *top; |
969 | struct buffer_head *bh; | 979 | struct buffer_head *bh; |
@@ -972,18 +982,8 @@ static struct buffer_head * ext3_dx_find_entry(struct inode *dir, | |||
972 | int namelen = entry->len; | 982 | int namelen = entry->len; |
973 | const u8 *name = entry->name; | 983 | const u8 *name = entry->name; |
974 | 984 | ||
975 | sb = dir->i_sb; | 985 | if (!(frame = dx_probe(entry, dir, &hinfo, frames, err))) |
976 | /* NFS may look up ".." - look at dx_root directory block */ | 986 | return NULL; |
977 | if (namelen > 2 || name[0] != '.'|| (namelen == 2 && name[1] != '.')) { | ||
978 | if (!(frame = dx_probe(entry, dir, &hinfo, frames, err))) | ||
979 | return NULL; | ||
980 | } else { | ||
981 | frame = frames; | ||
982 | frame->bh = NULL; /* for dx_release() */ | ||
983 | frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ | ||
984 | dx_set_block(frame->at, 0); /* dx_root block is 0 */ | ||
985 | } | ||
986 | hash = hinfo.hash; | ||
987 | do { | 987 | do { |
988 | block = dx_get_block(frame->at); | 988 | block = dx_get_block(frame->at); |
989 | if (!(bh = ext3_bread (NULL,dir, block, 0, err))) | 989 | if (!(bh = ext3_bread (NULL,dir, block, 0, err))) |
@@ -1009,7 +1009,7 @@ static struct buffer_head * ext3_dx_find_entry(struct inode *dir, | |||
1009 | } | 1009 | } |
1010 | brelse (bh); | 1010 | brelse (bh); |
1011 | /* Check to see if we should continue to search */ | 1011 | /* Check to see if we should continue to search */ |
1012 | retval = ext3_htree_next_block(dir, hash, frame, | 1012 | retval = ext3_htree_next_block(dir, hinfo.hash, frame, |
1013 | frames, NULL); | 1013 | frames, NULL); |
1014 | if (retval < 0) { | 1014 | if (retval < 0) { |
1015 | ext3_warning(sb, __func__, | 1015 | ext3_warning(sb, __func__, |