aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-11-25 14:09:20 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-11-25 16:28:33 -0500
commit2d51013ed2f2b6a5d2369b7fbbd989df1f6369e2 (patch)
treeb0e3f7fef15e3d1e109cffc90a709116b51d4f37 /fs
parenta26d79ca81d6e46c445c8db87a89740c9b4d17e9 (diff)
[PATCH] fuse: fix Oops in lookup
Fix bug in certain error paths of lookup routines. The request object was reused for sending FORGET, which is illegal. This bug could cause an Oops in 2.6.18. In earlier versions it might silently corrupt memory, but this is very unlikely. These error paths are never triggered by libfuse, so this wasn't noticed even with the 2.6.18 kernel, only with a filesystem using the raw kernel interface. Thanks to Russ Cox for the bug report and test filesystem. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/fuse/dir.c52
1 files changed, 38 insertions, 14 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index cfc8f81e60d0..c71a6c092ad9 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -138,6 +138,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
138 struct fuse_entry_out outarg; 138 struct fuse_entry_out outarg;
139 struct fuse_conn *fc; 139 struct fuse_conn *fc;
140 struct fuse_req *req; 140 struct fuse_req *req;
141 struct fuse_req *forget_req;
141 struct dentry *parent; 142 struct dentry *parent;
142 143
143 /* Doesn't hurt to "reset" the validity timeout */ 144 /* Doesn't hurt to "reset" the validity timeout */
@@ -152,25 +153,33 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
152 if (IS_ERR(req)) 153 if (IS_ERR(req))
153 return 0; 154 return 0;
154 155
156 forget_req = fuse_get_req(fc);
157 if (IS_ERR(forget_req)) {
158 fuse_put_request(fc, req);
159 return 0;
160 }
161
155 parent = dget_parent(entry); 162 parent = dget_parent(entry);
156 fuse_lookup_init(req, parent->d_inode, entry, &outarg); 163 fuse_lookup_init(req, parent->d_inode, entry, &outarg);
157 request_send(fc, req); 164 request_send(fc, req);
158 dput(parent); 165 dput(parent);
159 err = req->out.h.error; 166 err = req->out.h.error;
167 fuse_put_request(fc, req);
160 /* Zero nodeid is same as -ENOENT */ 168 /* Zero nodeid is same as -ENOENT */
161 if (!err && !outarg.nodeid) 169 if (!err && !outarg.nodeid)
162 err = -ENOENT; 170 err = -ENOENT;
163 if (!err) { 171 if (!err) {
164 struct fuse_inode *fi = get_fuse_inode(inode); 172 struct fuse_inode *fi = get_fuse_inode(inode);
165 if (outarg.nodeid != get_node_id(inode)) { 173 if (outarg.nodeid != get_node_id(inode)) {
166 fuse_send_forget(fc, req, outarg.nodeid, 1); 174 fuse_send_forget(fc, forget_req,
175 outarg.nodeid, 1);
167 return 0; 176 return 0;
168 } 177 }
169 spin_lock(&fc->lock); 178 spin_lock(&fc->lock);
170 fi->nlookup ++; 179 fi->nlookup ++;
171 spin_unlock(&fc->lock); 180 spin_unlock(&fc->lock);
172 } 181 }
173 fuse_put_request(fc, req); 182 fuse_put_request(fc, forget_req);
174 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) 183 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
175 return 0; 184 return 0;
176 185
@@ -221,6 +230,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
221 struct inode *inode = NULL; 230 struct inode *inode = NULL;
222 struct fuse_conn *fc = get_fuse_conn(dir); 231 struct fuse_conn *fc = get_fuse_conn(dir);
223 struct fuse_req *req; 232 struct fuse_req *req;
233 struct fuse_req *forget_req;
224 234
225 if (entry->d_name.len > FUSE_NAME_MAX) 235 if (entry->d_name.len > FUSE_NAME_MAX)
226 return ERR_PTR(-ENAMETOOLONG); 236 return ERR_PTR(-ENAMETOOLONG);
@@ -229,9 +239,16 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
229 if (IS_ERR(req)) 239 if (IS_ERR(req))
230 return ERR_PTR(PTR_ERR(req)); 240 return ERR_PTR(PTR_ERR(req));
231 241
242 forget_req = fuse_get_req(fc);
243 if (IS_ERR(forget_req)) {
244 fuse_put_request(fc, req);
245 return ERR_PTR(PTR_ERR(forget_req));
246 }
247
232 fuse_lookup_init(req, dir, entry, &outarg); 248 fuse_lookup_init(req, dir, entry, &outarg);
233 request_send(fc, req); 249 request_send(fc, req);
234 err = req->out.h.error; 250 err = req->out.h.error;
251 fuse_put_request(fc, req);
235 /* Zero nodeid is same as -ENOENT, but with valid timeout */ 252 /* Zero nodeid is same as -ENOENT, but with valid timeout */
236 if (!err && outarg.nodeid && 253 if (!err && outarg.nodeid &&
237 (invalid_nodeid(outarg.nodeid) || !valid_mode(outarg.attr.mode))) 254 (invalid_nodeid(outarg.nodeid) || !valid_mode(outarg.attr.mode)))
@@ -240,11 +257,11 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
240 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, 257 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
241 &outarg.attr); 258 &outarg.attr);
242 if (!inode) { 259 if (!inode) {
243 fuse_send_forget(fc, req, outarg.nodeid, 1); 260 fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
244 return ERR_PTR(-ENOMEM); 261 return ERR_PTR(-ENOMEM);
245 } 262 }
246 } 263 }
247 fuse_put_request(fc, req); 264 fuse_put_request(fc, forget_req);
248 if (err && err != -ENOENT) 265 if (err && err != -ENOENT)
249 return ERR_PTR(err); 266 return ERR_PTR(err);
250 267
@@ -388,6 +405,13 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
388 struct fuse_entry_out outarg; 405 struct fuse_entry_out outarg;
389 struct inode *inode; 406 struct inode *inode;
390 int err; 407 int err;
408 struct fuse_req *forget_req;
409
410 forget_req = fuse_get_req(fc);
411 if (IS_ERR(forget_req)) {
412 fuse_put_request(fc, req);
413 return PTR_ERR(forget_req);
414 }
391 415
392 req->in.h.nodeid = get_node_id(dir); 416 req->in.h.nodeid = get_node_id(dir);
393 req->out.numargs = 1; 417 req->out.numargs = 1;
@@ -395,24 +419,24 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
395 req->out.args[0].value = &outarg; 419 req->out.args[0].value = &outarg;
396 request_send(fc, req); 420 request_send(fc, req);
397 err = req->out.h.error; 421 err = req->out.h.error;
398 if (err) { 422 fuse_put_request(fc, req);
399 fuse_put_request(fc, req); 423 if (err)
400 return err; 424 goto out_put_forget_req;
401 } 425
402 err = -EIO; 426 err = -EIO;
403 if (invalid_nodeid(outarg.nodeid)) 427 if (invalid_nodeid(outarg.nodeid))
404 goto out_put_request; 428 goto out_put_forget_req;
405 429
406 if ((outarg.attr.mode ^ mode) & S_IFMT) 430 if ((outarg.attr.mode ^ mode) & S_IFMT)
407 goto out_put_request; 431 goto out_put_forget_req;
408 432
409 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, 433 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
410 &outarg.attr); 434 &outarg.attr);
411 if (!inode) { 435 if (!inode) {
412 fuse_send_forget(fc, req, outarg.nodeid, 1); 436 fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
413 return -ENOMEM; 437 return -ENOMEM;
414 } 438 }
415 fuse_put_request(fc, req); 439 fuse_put_request(fc, forget_req);
416 440
417 if (S_ISDIR(inode->i_mode)) { 441 if (S_ISDIR(inode->i_mode)) {
418 struct dentry *alias; 442 struct dentry *alias;
@@ -434,8 +458,8 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
434 fuse_invalidate_attr(dir); 458 fuse_invalidate_attr(dir);
435 return 0; 459 return 0;
436 460
437 out_put_request: 461 out_put_forget_req:
438 fuse_put_request(fc, req); 462 fuse_put_request(fc, forget_req);
439 return err; 463 return err;
440} 464}
441 465