VFS: fix locking bugs
.sync and fsync used unnecessarily restrictive locking type
.fsync violated locking order by obtaining a vmnt lock after a filp lock
.fsync contained a TOCTOU bug
.new_node violated locking rules (didn't upgrade lock upon file creation)
.do_pipe used unnecessarily restrictive locking type
.always lock pipes exclusively; even a read operation might require to do
a write on a vnode object (update pipe size)
.when opening a file with O_TRUNC, upgrade vnode lock when truncating
.utime used unnecessarily restrictive locking type
.path parsing:
.always acquire VMNT_WRITE or VMNT_EXCL on vmnt and downgrade to
VMNT_READ if that was what was actually requested. This prevents the
following deadlock scenario:
thread A:
lock_vmnt(vmp, TLL_READSER);
lock_vnode(vp, TLL_READSER);
upgrade_vmnt_lock(vmp, TLL_WRITE);
thread B:
lock_vmnt(vmp, TLL_READ);
lock_vnode(vp, TLL_READSER);
thread A will be stuck in upgrade_vmnt_lock and thread B is stuck in
lock_vnode. This happens when, for example, thread A tries create a
new node (open.c:new_node) and thread B tries to do eat_path to
change dir (stadir.c:do_chdir). When the path is being resolved, a
vnode is always locked with VNODE_OPCL (TLL_READSER) and then
downgraded to VNODE_READ if read-only is actually requested. Thread
A locks the vmnt with VMNT_WRITE (TLL_READSER) which still allows
VMNT_READ locks. Thread B can't acquire a lock on the vnode because
thread A has it; Thread A can't upgrade its vmnt lock to VMNT_WRITE
(TLL_WRITE) because thread B has a VMNT_READ lock on it.
By serializing vmnt locks during path parsing, thread B can only
acquire a lock on vmp when thread A has completely finished its
operation.
This commit is contained in:
+8
-6
@@ -304,7 +304,7 @@ int do_sync()
|
||||
int r = OK;
|
||||
|
||||
for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) {
|
||||
if ((r = lock_vmnt(vmp, VMNT_EXCL)) != OK)
|
||||
if ((r = lock_vmnt(vmp, VMNT_READ)) != OK)
|
||||
break;
|
||||
if (vmp->m_dev != NO_DEV && vmp->m_fs_e != NONE &&
|
||||
vmp->m_root_node != NULL) {
|
||||
@@ -331,20 +331,22 @@ int do_fsync()
|
||||
|
||||
if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_READ)) == NULL)
|
||||
return(err_code);
|
||||
|
||||
dev = rfilp->filp_vno->v_dev;
|
||||
unlock_filp(rfilp);
|
||||
|
||||
for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) {
|
||||
if (vmp->m_dev != dev) continue;
|
||||
if ((r = lock_vmnt(vmp, VMNT_READ)) != OK)
|
||||
break;
|
||||
if (vmp->m_dev != NO_DEV && vmp->m_dev == dev &&
|
||||
vmp->m_fs_e != NONE && vmp->m_root_node != NULL) {
|
||||
|
||||
if ((r = lock_vmnt(vmp, VMNT_EXCL)) != OK)
|
||||
break;
|
||||
req_sync(vmp->m_fs_e);
|
||||
unlock_vmnt(vmp);
|
||||
}
|
||||
unlock_vmnt(vmp);
|
||||
}
|
||||
|
||||
unlock_filp(rfilp);
|
||||
|
||||
return(r);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user