hpfs: deadlock and race in directory lseek()
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>
This commit is contained in:
parent
1d7095c72d
commit
31abdab9c1
|
@ -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)
|
if (whence == SEEK_DATA || whence == SEEK_HOLE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&i->i_mutex);
|
||||||
hpfs_lock(s);
|
hpfs_lock(s);
|
||||||
|
|
||||||
/*printk("dir lseek\n");*/
|
/*printk("dir lseek\n");*/
|
||||||
if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok;
|
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;
|
pos = ((loff_t) hpfs_de_as_down_as_possible(s, hpfs_inode->i_dno) << 4) + 1;
|
||||||
while (pos != new_off) {
|
while (pos != new_off) {
|
||||||
if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh);
|
if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh);
|
||||||
else goto fail;
|
else goto fail;
|
||||||
if (pos == 12) goto fail;
|
if (pos == 12) goto fail;
|
||||||
}
|
}
|
||||||
mutex_unlock(&i->i_mutex);
|
hpfs_add_pos(i, &filp->f_pos);
|
||||||
ok:
|
ok:
|
||||||
|
filp->f_pos = new_off;
|
||||||
hpfs_unlock(s);
|
hpfs_unlock(s);
|
||||||
return filp->f_pos = new_off;
|
|
||||||
fail:
|
|
||||||
mutex_unlock(&i->i_mutex);
|
mutex_unlock(&i->i_mutex);
|
||||||
|
return new_off;
|
||||||
|
fail:
|
||||||
/*printk("illegal lseek: %016llx\n", new_off);*/
|
/*printk("illegal lseek: %016llx\n", new_off);*/
|
||||||
hpfs_unlock(s);
|
hpfs_unlock(s);
|
||||||
|
mutex_unlock(&i->i_mutex);
|
||||||
return -ESPIPE;
|
return -ESPIPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue