summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorMarcel Telka <marcel.telka@nexenta.com>2014-12-15 17:38:51 +0100
committerDan McDonald <danmcd@omniti.com>2014-12-16 11:11:15 -0500
commit3d1d816f0f1e405a11a5871fb93eef11577c5fc8 (patch)
tree193dc59434e4d6acdbc9c0de2602c23c7e5c9570 /usr/src
parenta74909fd1b8a12f99797bbadb7a8af8bdca8596c (diff)
downloadillumos-gate-3d1d816f0f1e405a11a5871fb93eef11577c5fc8.tar.gz
5436 Panic due to bad mutex, from auth_cache being previously freed
Reviewed by: Garrett D'Amore <garrett@damore.org> Approved by: Dan McDonald <danmcd@omniti.com>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_auth.c69
1 files changed, 46 insertions, 23 deletions
diff --git a/usr/src/uts/common/fs/nfs/nfs_auth.c b/usr/src/uts/common/fs/nfs/nfs_auth.c
index 18d358795b..d18c6767fc 100644
--- a/usr/src/uts/common/fs/nfs/nfs_auth.c
+++ b/usr/src/uts/common/fs/nfs/nfs_auth.c
@@ -783,7 +783,7 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
struct netbuf *claddr;
struct auth_cache **head;
struct auth_cache *p;
- struct auth_cache *prev = NULL;
+ struct auth_cache *prev;
int access;
time_t refresh;
@@ -818,7 +818,9 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
rw_enter(&exi->exi_cache_lock, RW_READER);
head = &exi->exi_cache[hash(&addr)];
- for (p = *head; p; p = p->auth_next) {
+retry:
+ prev = NULL;
+ for (p = *head; p != NULL; p = p->auth_next) {
if (EQADDR(&addr, &p->auth_addr) && flavor == p->auth_flavor &&
crgetuid(cr) == p->auth_clnt_uid &&
crgetgid(cr) == p->auth_clnt_gid)
@@ -831,44 +833,65 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
* In a case the client's supplemental groups changed we need
* to discard the auth_cache entry and re-retrieve it.
*/
- mutex_enter(&p->auth_lock);
if (p->auth_clnt_ngids != crgetngroups(cr) ||
bcmp(p->auth_clnt_gids, crgetgroups(cr),
p->auth_clnt_ngids * sizeof (gid_t))) {
- auth_state_t prev_state = p->auth_state;
+ struct auth_cache *next;
- p->auth_state = NFS_AUTH_INVALID;
- mutex_exit(&p->auth_lock);
-
- if (prev_state == NFS_AUTH_FRESH) {
+ /*
+ * To remove the auth_cache entry from exi we need to
+ * hold the exi_cache_lock for write. If we do not
+ * have it yet, we will try to upgrade, or re-lock as a
+ * last resort. In a case of re-lock we will retry the
+ * search for the auth_cache entry because it might
+ * changed in the meantime.
+ */
+ ASSERT(RW_LOCK_HELD(&exi->exi_cache_lock));
+ if (rw_read_locked(&exi->exi_cache_lock) != 0) {
if (rw_tryupgrade(&exi->exi_cache_lock) == 0) {
- struct auth_cache *tmp;
-
rw_exit(&exi->exi_cache_lock);
rw_enter(&exi->exi_cache_lock,
RW_WRITER);
- prev = NULL;
- for (tmp = *head; tmp != NULL;
- tmp = tmp->auth_next) {
- if (p == tmp)
- break;
- prev = p;
- }
+ goto retry;
}
+ }
- if (prev == NULL)
- exi->exi_cache[hash(&addr)] =
- p->auth_next;
- else
- prev->auth_next = p->auth_next;
+ /*
+ * Now, remove the entry from exi and free it, or place
+ * it at the dead list.
+ */
+ next = p->auth_next;
+ mutex_enter(&p->auth_lock);
+ if (p->auth_state != NFS_AUTH_FRESH) {
+ p->auth_state = NFS_AUTH_INVALID;
+ mutex_exit(&p->auth_lock);
+ mutex_enter(&refreshq_lock);
+ p->auth_next = refreshq_dead_entries;
+ refreshq_dead_entries = p;
+ mutex_exit(&refreshq_lock);
+ } else {
+ mutex_exit(&p->auth_lock);
nfsauth_free_node(p);
}
+ /*
+ * Finally, disconnect the entry from exi
+ */
+ if (prev == NULL)
+ *head = next;
+ else
+ prev->auth_next = next;
+
goto retrieve;
}
- mutex_exit(&p->auth_lock);
+
+ /*
+ * If we hold the lock for write, downgrade
+ */
+ if (rw_read_locked(&exi->exi_cache_lock) == 0)
+ rw_downgrade(&exi->exi_cache_lock);
nfsauth_cache_hit++;