aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2015-06-08 01:57:31 -0400
committerJens Axboe <axboe@fb.com>2015-06-08 11:05:06 -0400
commit412a19b64ad17f7650ff778fd2cb9032938cf71f (patch)
treedfee77d911dc3814dd3b453977ee3e8183bcb50e
parent5857cd637bc0d60dc7e37af396b01324f199d89b (diff)
v9fs: fix error handling in v9fs_session_init()
On failure, v9fs_session_init() returns with the v9fs_session_info struct partially initialized and expects the caller to invoke v9fs_session_close() to clean it up; however, it doesn't track whether the bdi is initialized or not and curiously invokes bdi_destroy() in both vfs_session_init() failure path too. A. If v9fs_session_init() fails before the bdi is initialized, the follow-up v9fs_session_close() will invoke bdi_destroy() on an uninitialized bdi. B. If v9fs_session_init() fails after the bdi is initialized, bdi_destroy() will be called twice on the same bdi - once in the failure path of v9fs_session_init() and then by v9fs_session_close(). A is broken no matter what. B used to be okay because bdi_destroy() allowed being invoked multiple times on the same bdi, which BTW was broken in its own way - if bdi_destroy() was invoked on an initialiezd but !registered bdi, it'd fail to free percpu counters. Since f0054bb1e1f3 ("writeback: move backing_dev_info->wb_lock and ->worklist into bdi_writeback"), this no longer work - bdi_destroy() on an initialized but not registered bdi works correctly but multiple invocations of bdi_destroy() is no longer allowed. The obvious culprit here is v9fs_session_init()'s odd and broken error behavior. It should simply clean up after itself on failures. This patch makes the following updates to v9fs_session_init(). * @rc -> @retval error return propagation removed. It didn't serve any purpose. Just use @rc. * Move addition to v9fs_sessionlist to the end of the function so that incomplete sessions are not put on the list or iterated and error path doesn't have to worry about it. * Update error handling so that it cleans up after itself. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r--fs/9p/v9fs.c50
-rw-r--r--fs/9p/vfs_super.c8
2 files changed, 24 insertions, 34 deletions
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 620d93489539..8aa56bb6e861 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -320,31 +320,21 @@ fail_option_alloc:
320struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, 320struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
321 const char *dev_name, char *data) 321 const char *dev_name, char *data)
322{ 322{
323 int retval = -EINVAL;
324 struct p9_fid *fid; 323 struct p9_fid *fid;
325 int rc; 324 int rc = -ENOMEM;
326 325
327 v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL); 326 v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
328 if (!v9ses->uname) 327 if (!v9ses->uname)
329 return ERR_PTR(-ENOMEM); 328 goto err_names;
330 329
331 v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL); 330 v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
332 if (!v9ses->aname) { 331 if (!v9ses->aname)
333 kfree(v9ses->uname); 332 goto err_names;
334 return ERR_PTR(-ENOMEM);
335 }
336 init_rwsem(&v9ses->rename_sem); 333 init_rwsem(&v9ses->rename_sem);
337 334
338 rc = bdi_setup_and_register(&v9ses->bdi, "9p"); 335 rc = bdi_setup_and_register(&v9ses->bdi, "9p");
339 if (rc) { 336 if (rc)
340 kfree(v9ses->aname); 337 goto err_names;
341 kfree(v9ses->uname);
342 return ERR_PTR(rc);
343 }
344
345 spin_lock(&v9fs_sessionlist_lock);
346 list_add(&v9ses->slist, &v9fs_sessionlist);
347 spin_unlock(&v9fs_sessionlist_lock);
348 338
349 v9ses->uid = INVALID_UID; 339 v9ses->uid = INVALID_UID;
350 v9ses->dfltuid = V9FS_DEFUID; 340 v9ses->dfltuid = V9FS_DEFUID;
@@ -352,10 +342,9 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
352 342
353 v9ses->clnt = p9_client_create(dev_name, data); 343 v9ses->clnt = p9_client_create(dev_name, data);
354 if (IS_ERR(v9ses->clnt)) { 344 if (IS_ERR(v9ses->clnt)) {
355 retval = PTR_ERR(v9ses->clnt); 345 rc = PTR_ERR(v9ses->clnt);
356 v9ses->clnt = NULL;
357 p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n"); 346 p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
358 goto error; 347 goto err_bdi;
359 } 348 }
360 349
361 v9ses->flags = V9FS_ACCESS_USER; 350 v9ses->flags = V9FS_ACCESS_USER;
@@ -368,10 +357,8 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
368 } 357 }
369 358
370 rc = v9fs_parse_options(v9ses, data); 359 rc = v9fs_parse_options(v9ses, data);
371 if (rc < 0) { 360 if (rc < 0)
372 retval = rc; 361 goto err_clnt;
373 goto error;
374 }
375 362
376 v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; 363 v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
377 364
@@ -405,10 +392,9 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
405 fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID, 392 fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
406 v9ses->aname); 393 v9ses->aname);
407 if (IS_ERR(fid)) { 394 if (IS_ERR(fid)) {
408 retval = PTR_ERR(fid); 395 rc = PTR_ERR(fid);
409 fid = NULL;
410 p9_debug(P9_DEBUG_ERROR, "cannot attach\n"); 396 p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
411 goto error; 397 goto err_clnt;
412 } 398 }
413 399
414 if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE) 400 if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
@@ -420,12 +406,20 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
420 /* register the session for caching */ 406 /* register the session for caching */
421 v9fs_cache_session_get_cookie(v9ses); 407 v9fs_cache_session_get_cookie(v9ses);
422#endif 408#endif
409 spin_lock(&v9fs_sessionlist_lock);
410 list_add(&v9ses->slist, &v9fs_sessionlist);
411 spin_unlock(&v9fs_sessionlist_lock);
423 412
424 return fid; 413 return fid;
425 414
426error: 415err_clnt:
416 p9_client_destroy(v9ses->clnt);
417err_bdi:
427 bdi_destroy(&v9ses->bdi); 418 bdi_destroy(&v9ses->bdi);
428 return ERR_PTR(retval); 419err_names:
420 kfree(v9ses->uname);
421 kfree(v9ses->aname);
422 return ERR_PTR(rc);
429} 423}
430 424
431/** 425/**
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index e99a338a4638..bf495cedec26 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -130,11 +130,7 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
130 fid = v9fs_session_init(v9ses, dev_name, data); 130 fid = v9fs_session_init(v9ses, dev_name, data);
131 if (IS_ERR(fid)) { 131 if (IS_ERR(fid)) {
132 retval = PTR_ERR(fid); 132 retval = PTR_ERR(fid);
133 /* 133 goto free_session;
134 * we need to call session_close to tear down some
135 * of the data structure setup by session_init
136 */
137 goto close_session;
138 } 134 }
139 135
140 sb = sget(fs_type, NULL, v9fs_set_super, flags, v9ses); 136 sb = sget(fs_type, NULL, v9fs_set_super, flags, v9ses);
@@ -195,8 +191,8 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
195 191
196clunk_fid: 192clunk_fid:
197 p9_client_clunk(fid); 193 p9_client_clunk(fid);
198close_session:
199 v9fs_session_close(v9ses); 194 v9fs_session_close(v9ses);
195free_session:
200 kfree(v9ses); 196 kfree(v9ses);
201 return ERR_PTR(retval); 197 return ERR_PTR(retval);
202 198