diff options
author | Tejun Heo <tj@kernel.org> | 2015-06-08 01:57:31 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2015-06-08 11:05:06 -0400 |
commit | 412a19b64ad17f7650ff778fd2cb9032938cf71f (patch) | |
tree | dfee77d911dc3814dd3b453977ee3e8183bcb50e | |
parent | 5857cd637bc0d60dc7e37af396b01324f199d89b (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.c | 50 | ||||
-rw-r--r-- | fs/9p/vfs_super.c | 8 |
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: | |||
320 | struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, | 320 | struct 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 | ||
426 | error: | 415 | err_clnt: |
416 | p9_client_destroy(v9ses->clnt); | ||
417 | err_bdi: | ||
427 | bdi_destroy(&v9ses->bdi); | 418 | bdi_destroy(&v9ses->bdi); |
428 | return ERR_PTR(retval); | 419 | err_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 | ||
196 | clunk_fid: | 192 | clunk_fid: |
197 | p9_client_clunk(fid); | 193 | p9_client_clunk(fid); |
198 | close_session: | ||
199 | v9fs_session_close(v9ses); | 194 | v9fs_session_close(v9ses); |
195 | free_session: | ||
200 | kfree(v9ses); | 196 | kfree(v9ses); |
201 | return ERR_PTR(retval); | 197 | return ERR_PTR(retval); |
202 | 198 | ||