diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-05-18 02:38:52 -0400 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2014-04-02 00:59:00 +0100 |
commit | b89ff066efab68d53a3a060b2c7d9089f8afa8ea (patch) | |
tree | 6fd67b004c4a6b3b215dd9212825b15b858d11da /fs | |
parent | 578d1903dcfd8911534aa602ea8c104c383fadda (diff) | |
download | lwn-b89ff066efab68d53a3a060b2c7d9089f8afa8ea.tar.gz lwn-b89ff066efab68d53a3a060b2c7d9089f8afa8ea.zip |
hpfs: deadlock and race in directory lseek()
commit 31abdab9c11bb1694ecd1476a7edbe8e964d94ac upstream.
For one thing, there's an ABBA deadlock on hpfs fs-wide lock and i_mutex
in hpfs_dir_lseek() - there's a lot of methods that grab the former with
the caller already holding the latter, so it must take i_mutex first.
For another, locking the damn thing, carefully validating the offset,
then dropping locks and assigning the offset is obviously racy.
Moreover, we _must_ do hpfs_add_pos(), or the machinery in dnode.c
won't modify the sucker on B-tree surgeries.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/hpfs/dir.c | 10 |
1 files changed, 6 insertions, 4 deletions
diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 2fa0089a02a8..46549c778001 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -33,25 +33,27 @@ static loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence) if (whence == SEEK_DATA || whence == SEEK_HOLE) return -EINVAL; + mutex_lock(&i->i_mutex); hpfs_lock(s); /*printk("dir lseek\n");*/ if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok; - mutex_lock(&i->i_mutex); pos = ((loff_t) hpfs_de_as_down_as_possible(s, hpfs_inode->i_dno) << 4) + 1; while (pos != new_off) { if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh); else goto fail; if (pos == 12) goto fail; } - mutex_unlock(&i->i_mutex); + hpfs_add_pos(i, &filp->f_pos); ok: + filp->f_pos = new_off; hpfs_unlock(s); - return filp->f_pos = new_off; -fail: mutex_unlock(&i->i_mutex); + return new_off; +fail: /*printk("illegal lseek: %016llx\n", new_off);*/ hpfs_unlock(s); + mutex_unlock(&i->i_mutex); return -ESPIPE; } |