aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext3/namei.c
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2010-10-16 19:36:59 -0400
committerJan Kara <jack@suse.cz>2011-01-06 05:52:15 -0500
commitf0cad89f5e8ef8b6d0c065115565524137e44f0b (patch)
tree8eea669cb0317a2894edff505b132c5d3eb6fcfb /fs/ext3/namei.c
parentad692bf3ea035fa5a9d56462cf3df97d9607cced (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.c30
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__,