From e2a099cf1b1188b60aecc18de449444f7dca0f6a Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Fri, 1 Feb 2013 13:47:37 -0800 Subject: [PATCH] kernel - Fix kernel panic caused by rename race * kern_rename() must temporarily unlock the source ncp when resolving the target ncp, and will sometimes have to temporarily unlock the target ncp when relocking both. This can race against a topological removal of one or both ncp's, resulting in incorrect operation or a panic due to a NULL vp. * Detect the case and return EAGAIN if the ncp's have been marked NCF_DESTROYED or if fromncp loses its vp resolution. Callers detect the EAGAIN and retry the operation. Reported-by: ftigeot --- sys/emulation/linux/linux_file.c | 18 +++++----- sys/kern/vfs_syscalls.c | 58 ++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/sys/emulation/linux/linux_file.c b/sys/emulation/linux/linux_file.c index c3652ec7f1..14490155db 100644 --- a/sys/emulation/linux/linux_file.c +++ b/sys/emulation/linux/linux_file.c @@ -803,14 +803,16 @@ sys_linux_rename(struct linux_rename_args *args) kprintf(ARGS(rename, "%s, %s"), from, to); #endif get_mplock(); - error = nlookup_init(&fromnd, from, UIO_SYSSPACE, 0); - if (error == 0) { - error = nlookup_init(&tond, to, UIO_SYSSPACE, 0); - if (error == 0) - error = kern_rename(&fromnd, &tond); - nlookup_done(&tond); - } - nlookup_done(&fromnd); + do { + error = nlookup_init(&fromnd, from, UIO_SYSSPACE, 0); + if (error == 0) { + error = nlookup_init(&tond, to, UIO_SYSSPACE, 0); + if (error == 0) + error = kern_rename(&fromnd, &tond); + nlookup_done(&tond); + } + nlookup_done(&fromnd); + } while (error == EAGAIN); rel_mplock(); linux_free_path(&from); linux_free_path(&to); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index dc144d2392..53cf1bf24e 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -3693,6 +3693,22 @@ kern_rename(struct nlookupdata *fromnd, struct nlookupdata *tond) &tond->nl_nch, tond->nl_cred); fromnd->nl_flags |= NLC_NCPISLOCKED; + /* + * If either fromnd or tond are marked destroyed a ripout occured + * out from under us and we must retry. + */ + if ((fromnd->nl_nch.ncp->nc_flag & (NCF_DESTROYED | NCF_UNRESOLVED)) || + fromnd->nl_nch.ncp->nc_vp == NULL || + (tond->nl_nch.ncp->nc_flag & NCF_DESTROYED)) { + kprintf("kern_rename: retry due to ripout on: " + "\"%s\" -> \"%s\"\n", + fromnd->nl_nch.ncp->nc_name, + tond->nl_nch.ncp->nc_name); + cache_drop(&fnchd); + cache_drop(&tnchd); + return (EAGAIN); + } + /* * make sure the parent directories linkages are the same */ @@ -3798,14 +3814,16 @@ sys_rename(struct rename_args *uap) struct nlookupdata fromnd, tond; int error; - error = nlookup_init(&fromnd, uap->from, UIO_USERSPACE, 0); - if (error == 0) { - error = nlookup_init(&tond, uap->to, UIO_USERSPACE, 0); - if (error == 0) - error = kern_rename(&fromnd, &tond); - nlookup_done(&tond); - } - nlookup_done(&fromnd); + do { + error = nlookup_init(&fromnd, uap->from, UIO_USERSPACE, 0); + if (error == 0) { + error = nlookup_init(&tond, uap->to, UIO_USERSPACE, 0); + if (error == 0) + error = kern_rename(&fromnd, &tond); + nlookup_done(&tond); + } + nlookup_done(&fromnd); + } while (error == EAGAIN); return (error); } @@ -3823,16 +3841,20 @@ sys_renameat(struct renameat_args *uap) struct file *oldfp, *newfp; int error; - error = nlookup_init_at(&oldnd, &oldfp, uap->oldfd, uap->old, - UIO_USERSPACE, 0); - if (error == 0) { - error = nlookup_init_at(&newnd, &newfp, uap->newfd, uap->new, - UIO_USERSPACE, 0); - if (error == 0) - error = kern_rename(&oldnd, &newnd); - nlookup_done_at(&newnd, newfp); - } - nlookup_done_at(&oldnd, oldfp); + do { + error = nlookup_init_at(&oldnd, &oldfp, + uap->oldfd, uap->old, + UIO_USERSPACE, 0); + if (error == 0) { + error = nlookup_init_at(&newnd, &newfp, + uap->newfd, uap->new, + UIO_USERSPACE, 0); + if (error == 0) + error = kern_rename(&oldnd, &newnd); + nlookup_done_at(&newnd, newfp); + } + nlookup_done_at(&oldnd, oldfp); + } while (error == EAGAIN); return (error); } -- 2.41.0