aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexey Khoroshilov <khoroshilov@ispras.ru>2013-04-30 18:27:52 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-30 20:04:05 -0400
commit9509f17851da294f8ecf0fc0bfe0fe609671352d (patch)
treef1cbc43a20bf1d4d81fb31d0dedf9ad4463bda60
parenteb53b6db7a53642b80b0ca4885cb91d5c7dbc0f8 (diff)
hfs: add error checking for hfs_find_init()
hfs_find_init() may fail with ENOMEM, but there are places, where the returned value is not checked. The consequences can be very unpleasant, e.g. kfree uninitialized pointer and inappropriate mutex unlocking. The patch adds checks for errors in hfs_find_init(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov <khoroshilov@ispras.ru> Reviewed-by: Vyacheslav Dubeyko <slava@dubeyko.com> Cc: Hin-Tak Leung <htl10@users.sourceforge.net> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Cc: Christoph Hellwig <hch@lst.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/hfs/catalog.c12
-rw-r--r--fs/hfs/dir.c8
-rw-r--r--fs/hfs/extent.c48
-rw-r--r--fs/hfs/hfs_fs.h2
-rw-r--r--fs/hfs/inode.c11
-rw-r--r--fs/hfs/super.c4
6 files changed, 61 insertions, 24 deletions
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
index 424b0337f524..9569b39257ec 100644
--- a/fs/hfs/catalog.c
+++ b/fs/hfs/catalog.c
@@ -92,7 +92,9 @@ int hfs_cat_create(u32 cnid, struct inode *dir, struct qstr *str, struct inode *
92 return -ENOSPC; 92 return -ENOSPC;
93 93
94 sb = dir->i_sb; 94 sb = dir->i_sb;
95 hfs_find_init(HFS_SB(sb)->cat_tree, &fd); 95 err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
96 if (err)
97 return err;
96 98
97 hfs_cat_build_key(sb, fd.search_key, cnid, NULL); 99 hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
98 entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ? 100 entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ?
@@ -214,7 +216,9 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, struct qstr *str)
214 216
215 dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid); 217 dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
216 sb = dir->i_sb; 218 sb = dir->i_sb;
217 hfs_find_init(HFS_SB(sb)->cat_tree, &fd); 219 res = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
220 if (res)
221 return res;
218 222
219 hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str); 223 hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str);
220 res = hfs_brec_find(&fd); 224 res = hfs_brec_find(&fd);
@@ -281,7 +285,9 @@ int hfs_cat_move(u32 cnid, struct inode *src_dir, struct qstr *src_name,
281 dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name, 285 dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name,
282 dst_dir->i_ino, dst_name->name); 286 dst_dir->i_ino, dst_name->name);
283 sb = src_dir->i_sb; 287 sb = src_dir->i_sb;
284 hfs_find_init(HFS_SB(sb)->cat_tree, &src_fd); 288 err = hfs_find_init(HFS_SB(sb)->cat_tree, &src_fd);
289 if (err)
290 return err;
285 dst_fd = src_fd; 291 dst_fd = src_fd;
286 292
287 /* find the old dir entry and read the data */ 293 /* find the old dir entry and read the data */
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index 5f7f1abd5f6d..e1c80482a292 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -25,7 +25,9 @@ static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
25 struct inode *inode = NULL; 25 struct inode *inode = NULL;
26 int res; 26 int res;
27 27
28 hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); 28 res = hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
29 if (res)
30 return ERR_PTR(res);
29 hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name); 31 hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
30 res = hfs_brec_read(&fd, &rec, sizeof(rec)); 32 res = hfs_brec_read(&fd, &rec, sizeof(rec));
31 if (res) { 33 if (res) {
@@ -63,7 +65,9 @@ static int hfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
63 if (filp->f_pos >= inode->i_size) 65 if (filp->f_pos >= inode->i_size)
64 return 0; 66 return 0;
65 67
66 hfs_find_init(HFS_SB(sb)->cat_tree, &fd); 68 err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
69 if (err)
70 return err;
67 hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); 71 hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
68 err = hfs_brec_find(&fd); 72 err = hfs_brec_find(&fd);
69 if (err) 73 if (err)
diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c
index a67955a0c36f..813447b5b052 100644
--- a/fs/hfs/extent.c
+++ b/fs/hfs/extent.c
@@ -107,7 +107,7 @@ static u16 hfs_ext_lastblock(struct hfs_extent *ext)
107 return be16_to_cpu(ext->block) + be16_to_cpu(ext->count); 107 return be16_to_cpu(ext->block) + be16_to_cpu(ext->count);
108} 108}
109 109
110static void __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) 110static int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
111{ 111{
112 int res; 112 int res;
113 113
@@ -116,26 +116,31 @@ static void __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd
116 res = hfs_brec_find(fd); 116 res = hfs_brec_find(fd);
117 if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) { 117 if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) {
118 if (res != -ENOENT) 118 if (res != -ENOENT)
119 return; 119 return res;
120 hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec)); 120 hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
121 HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); 121 HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
122 } else { 122 } else {
123 if (res) 123 if (res)
124 return; 124 return res;
125 hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength); 125 hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength);
126 HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY; 126 HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY;
127 } 127 }
128 return 0;
128} 129}
129 130
130void hfs_ext_write_extent(struct inode *inode) 131int hfs_ext_write_extent(struct inode *inode)
131{ 132{
132 struct hfs_find_data fd; 133 struct hfs_find_data fd;
134 int res = 0;
133 135
134 if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) { 136 if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
135 hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); 137 res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
136 __hfs_ext_write_extent(inode, &fd); 138 if (res)
139 return res;
140 res = __hfs_ext_write_extent(inode, &fd);
137 hfs_find_exit(&fd); 141 hfs_find_exit(&fd);
138 } 142 }
143 return res;
139} 144}
140 145
141static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent, 146static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent,
@@ -161,8 +166,11 @@ static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode
161{ 166{
162 int res; 167 int res;
163 168
164 if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) 169 if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
165 __hfs_ext_write_extent(inode, fd); 170 res = __hfs_ext_write_extent(inode, fd);
171 if (res)
172 return res;
173 }
166 174
167 res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino, 175 res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino,
168 block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); 176 block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA);
@@ -185,9 +193,11 @@ static int hfs_ext_read_extent(struct inode *inode, u16 block)
185 block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks) 193 block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks)
186 return 0; 194 return 0;
187 195
188 hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); 196 res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
189 res = __hfs_ext_cache_extent(&fd, inode, block); 197 if (!res) {
190 hfs_find_exit(&fd); 198 res = __hfs_ext_cache_extent(&fd, inode, block);
199 hfs_find_exit(&fd);
200 }
191 return res; 201 return res;
192} 202}
193 203
@@ -298,7 +308,9 @@ int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type)
298 if (total_blocks == blocks) 308 if (total_blocks == blocks)
299 return 0; 309 return 0;
300 310
301 hfs_find_init(HFS_SB(sb)->ext_tree, &fd); 311 res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
312 if (res)
313 return res;
302 do { 314 do {
303 res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type); 315 res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type);
304 if (res) 316 if (res)
@@ -438,7 +450,9 @@ out:
438 450
439insert_extent: 451insert_extent:
440 dprint(DBG_EXTENT, "insert new extent\n"); 452 dprint(DBG_EXTENT, "insert new extent\n");
441 hfs_ext_write_extent(inode); 453 res = hfs_ext_write_extent(inode);
454 if (res)
455 goto out;
442 456
443 memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec)); 457 memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec));
444 HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start); 458 HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start);
@@ -466,7 +480,6 @@ void hfs_file_truncate(struct inode *inode)
466 struct address_space *mapping = inode->i_mapping; 480 struct address_space *mapping = inode->i_mapping;
467 void *fsdata; 481 void *fsdata;
468 struct page *page; 482 struct page *page;
469 int res;
470 483
471 /* XXX: Can use generic_cont_expand? */ 484 /* XXX: Can use generic_cont_expand? */
472 size = inode->i_size - 1; 485 size = inode->i_size - 1;
@@ -488,7 +501,12 @@ void hfs_file_truncate(struct inode *inode)
488 goto out; 501 goto out;
489 502
490 mutex_lock(&HFS_I(inode)->extents_lock); 503 mutex_lock(&HFS_I(inode)->extents_lock);
491 hfs_find_init(HFS_SB(sb)->ext_tree, &fd); 504 res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
505 if (res) {
506 mutex_unlock(&HFS_I(inode)->extents_lock);
507 /* XXX: We lack error handling of hfs_file_truncate() */
508 return;
509 }
492 while (1) { 510 while (1) {
493 if (alloc_cnt == HFS_I(inode)->first_blocks) { 511 if (alloc_cnt == HFS_I(inode)->first_blocks) {
494 hfs_free_extents(sb, HFS_I(inode)->first_extents, 512 hfs_free_extents(sb, HFS_I(inode)->first_extents,
diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index 693df9fe52b2..67817af96f82 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -174,7 +174,7 @@ extern const struct inode_operations hfs_dir_inode_operations;
174/* extent.c */ 174/* extent.c */
175extern int hfs_ext_keycmp(const btree_key *, const btree_key *); 175extern int hfs_ext_keycmp(const btree_key *, const btree_key *);
176extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int); 176extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int);
177extern void hfs_ext_write_extent(struct inode *); 177extern int hfs_ext_write_extent(struct inode *);
178extern int hfs_extend_file(struct inode *); 178extern int hfs_extend_file(struct inode *);
179extern void hfs_file_truncate(struct inode *); 179extern void hfs_file_truncate(struct inode *);
180 180
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 3031dfdd2358..0847471ff04f 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -416,9 +416,12 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
416 struct inode *main_inode = inode; 416 struct inode *main_inode = inode;
417 struct hfs_find_data fd; 417 struct hfs_find_data fd;
418 hfs_cat_rec rec; 418 hfs_cat_rec rec;
419 int res;
419 420
420 dprint(DBG_INODE, "hfs_write_inode: %lu\n", inode->i_ino); 421 dprint(DBG_INODE, "hfs_write_inode: %lu\n", inode->i_ino);
421 hfs_ext_write_extent(inode); 422 res = hfs_ext_write_extent(inode);
423 if (res)
424 return res;
422 425
423 if (inode->i_ino < HFS_FIRSTUSER_CNID) { 426 if (inode->i_ino < HFS_FIRSTUSER_CNID) {
424 switch (inode->i_ino) { 427 switch (inode->i_ino) {
@@ -515,7 +518,11 @@ static struct dentry *hfs_file_lookup(struct inode *dir, struct dentry *dentry,
515 if (!inode) 518 if (!inode)
516 return ERR_PTR(-ENOMEM); 519 return ERR_PTR(-ENOMEM);
517 520
518 hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); 521 res = hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
522 if (res) {
523 iput(inode);
524 return ERR_PTR(res);
525 }
519 fd.search_key->cat = HFS_I(dir)->cat_key; 526 fd.search_key->cat = HFS_I(dir)->cat_key;
520 res = hfs_brec_read(&fd, &rec, sizeof(rec)); 527 res = hfs_brec_read(&fd, &rec, sizeof(rec));
521 if (!res) { 528 if (!res) {
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index bbaaa8a4ee64..719760b2b0a6 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -418,7 +418,9 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent)
418 } 418 }
419 419
420 /* try to get the root inode */ 420 /* try to get the root inode */
421 hfs_find_init(HFS_SB(sb)->cat_tree, &fd); 421 res = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
422 if (res)
423 goto bail_no_root;
422 res = hfs_cat_find_brec(sb, HFS_ROOT_CNID, &fd); 424 res = hfs_cat_find_brec(sb, HFS_ROOT_CNID, &fd);
423 if (!res) { 425 if (!res) {
424 if (fd.entrylength > sizeof(rec) || fd.entrylength < 0) { 426 if (fd.entrylength > sizeof(rec) || fd.entrylength < 0) {