diff options
author | cg209009 <none@none> | 2007-07-02 10:10:24 -0700 |
---|---|---|
committer | cg209009 <none@none> | 2007-07-02 10:10:24 -0700 |
commit | ed0ee1acd17669b970ef56ec519cbd4e25ed9366 (patch) | |
tree | 7e40ba10fe02d1e7bb1385006e2e405fe48d3e6a /usr/src/uts/common/syscall/uadmin.c | |
parent | c151b7fa492e1f62c5460a2f05ba0311a16e09aa (diff) | |
download | illumos-gate-ed0ee1acd17669b970ef56ec519cbd4e25ed9366.tar.gz |
6566758 Race condition in uadmin(A_SHUTDOWN)
Diffstat (limited to 'usr/src/uts/common/syscall/uadmin.c')
-rw-r--r-- | usr/src/uts/common/syscall/uadmin.c | 48 |
1 files changed, 38 insertions, 10 deletions
diff --git a/usr/src/uts/common/syscall/uadmin.c b/usr/src/uts/common/syscall/uadmin.c index 9946bd4b22..df00b4e9d0 100644 --- a/usr/src/uts/common/syscall/uadmin.c +++ b/usr/src/uts/common/syscall/uadmin.c @@ -52,6 +52,8 @@ #include <sys/sunddi.h> #include <sys/policy.h> #include <sys/zone.h> +#include <sys/condvar.h> +#include <sys/thread.h> /* * Administrivia system call. We provide this in two flavors: one for calling @@ -62,6 +64,8 @@ extern ksema_t fsflush_sema; kmutex_t ualock; +kcondvar_t uacond; +kthread_t *ua_shutdown_thread = NULL; int sys_shutdown = 0; @@ -123,7 +127,6 @@ int kadmin(int cmd, int fcn, void *mdep, cred_t *credp) { int error = 0; - int locked = 0; char *buf; size_t buflen = 0; boolean_t invoke_cb = B_FALSE; @@ -153,13 +156,30 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) } /* - * Serialize these operations on ualock. If it is held, just return - * as if successful since the system will soon reset or remount. + * Serialize these operations on ualock. If it is held, the + * system should shutdown, reboot, or remount shortly, unless there is + * an error. We need a cv rather than just a mutex because proper + * functioning of A_REBOOT relies on being able to interrupt blocked + * userland callers. + * + * We only clear ua_shutdown_thread after A_REMOUNT, because A_SHUTDOWN + * and A_REBOOT should never return. */ if (cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_REMOUNT) { - if (!mutex_tryenter(&ualock)) - return (0); - locked = 1; + mutex_enter(&ualock); + while (ua_shutdown_thread != NULL) { + if (cv_wait_sig(&uacond, &ualock) == 0) { + /* + * If we were interrupted, leave, and handle + * the signal (or exit, depending on what + * happened) + */ + mutex_exit(&ualock); + return (EINTR); + } + } + ua_shutdown_thread = curthread; + mutex_exit(&ualock); } switch (cmd) { @@ -175,7 +195,13 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) if (p != &p0) { proc_is_exiting(p); if ((error = exitlwps(0)) != 0) { - ASSERT(locked); + /* + * Another thread in this process also called + * exitlwps(). + */ + mutex_enter(&ualock); + ua_shutdown_thread = NULL; + cv_signal(&uacond); mutex_exit(&ualock); return (error); } @@ -274,6 +300,11 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) case A_REMOUNT: (void) VFS_MOUNTROOT(rootvfs, ROOT_REMOUNT); + /* Let other threads enter the shutdown path now */ + mutex_enter(&ualock); + ua_shutdown_thread = NULL; + cv_signal(&uacond); + mutex_exit(&ualock); break; case A_FREEZE: @@ -326,9 +357,6 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp) error = EINVAL; } - if (locked) - mutex_exit(&ualock); - return (error); } |