summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authordeischen <deischen@FreeBSD.org>2003-05-04 22:29:09 +0000
committerdeischen <deischen@FreeBSD.org>2003-05-04 22:29:09 +0000
commit3cb9ba9e43902d7a4cf096e0b6cd3f12eda1b92c (patch)
treef349be58e219d80c1d742a23da9f54ca59e6b6c9 /lib
parent9328ad6bf8f2652faa7b7568504c1abe7e4b513a (diff)
downloadFreeBSD-src-3cb9ba9e43902d7a4cf096e0b6cd3f12eda1b92c.zip
FreeBSD-src-3cb9ba9e43902d7a4cf096e0b6cd3f12eda1b92c.tar.gz
Protect against a race between granting a lock and accessing
other parts of the lock. Submitted by: davidxu
Diffstat (limited to 'lib')
-rw-r--r--lib/libkse/sys/lock.c34
-rw-r--r--lib/libkse/sys/lock.h1
-rw-r--r--lib/libpthread/sys/lock.c34
-rw-r--r--lib/libpthread/sys/lock.h1
4 files changed, 56 insertions, 14 deletions
diff --git a/lib/libkse/sys/lock.c b/lib/libkse/sys/lock.c
index c77dfcb..12ce1a0 100644
--- a/lib/libkse/sys/lock.c
+++ b/lib/libkse/sys/lock.c
@@ -65,6 +65,7 @@ _lock_init(struct lock *lck, enum lock_type ltype,
lck->l_head->lr_watcher = NULL;
lck->l_head->lr_owner = NULL;
lck->l_head->lr_waiting = 0;
+ lck->l_head->lr_handshake = 0;
lck->l_tail = lck->l_head;
}
return (0);
@@ -84,6 +85,7 @@ _lockuser_init(struct lockuser *lu, void *priv)
lu->lu_myreq->lr_watcher = NULL;
lu->lu_myreq->lr_owner = lu;
lu->lu_myreq->lr_waiting = 0;
+ lu->lu_myreq->lr_handshake = 0;
lu->lu_watchreq = NULL;
lu->lu_priority = 0;
lu->lu_private = priv;
@@ -169,6 +171,12 @@ _lock_acquire(struct lock *lck, struct lockuser *lu, int prio)
while (lu->lu_watchreq->lr_locked != 0)
lck->l_wait(lck, lu);
atomic_store_rel_long(&lu->lu_watchreq->lr_waiting, 0);
+ /*
+ * Wait for original owner to stop accessing the
+ * lockreq object.
+ */
+ while (lu->lu_watchreq->lr_handshake)
+ ;
}
}
}
@@ -232,19 +240,24 @@ _lock_release(struct lock *lck, struct lockuser *lu)
}
}
if (lu_h != NULL) {
+ lu_h->lu_watchreq->lr_handshake = 1;
/* Give the lock to the highest priority user. */
atomic_store_rel_long(&lu_h->lu_watchreq->lr_locked, 0);
if ((lu_h->lu_watchreq->lr_waiting != 0) &&
(lck->l_wakeup != NULL))
/* Notify the sleeper */
lck->l_wakeup(lck, lu_h->lu_myreq->lr_watcher);
+ atomic_store_rel_long(&lu_h->lu_watchreq->lr_handshake,
+ 0);
} else {
+ myreq->lr_handshake = 1;
/* Give the lock to the previous request. */
atomic_store_rel_long(&myreq->lr_locked, 0);
if ((myreq->lr_waiting != 0) &&
(lck->l_wakeup != NULL))
/* Notify the sleeper */
lck->l_wakeup(lck, myreq->lr_watcher);
+ atomic_store_rel_long(&myreq->lr_handshake, 0);
}
} else {
/*
@@ -257,12 +270,19 @@ _lock_release(struct lock *lck, struct lockuser *lu)
lu->lu_watchreq = NULL;
lu->lu_myreq->lr_locked = 1;
lu->lu_myreq->lr_waiting = 0;
- /* Give the lock to the previous request. */
- atomic_store_rel_long(&myreq->lr_locked, 0);
- if ((myreq->lr_waiting != 0) &&
- (lck->l_wakeup != NULL))
- /* Notify the sleeper */
- lck->l_wakeup(lck, myreq->lr_watcher);
-
+ if (lck->l_wakeup) {
+ /* Start wakeup */
+ myreq->lr_handshake = 1;
+ /* Give the lock to the previous request. */
+ atomic_store_rel_long(&myreq->lr_locked, 0);
+ if (myreq->lr_waiting != 0) {
+ /* Notify the sleeper */
+ lck->l_wakeup(lck, myreq->lr_watcher);
+ }
+ /* Stop wakeup */
+ atomic_store_rel_long(&myreq->lr_handshake, 0);
+ } else {
+ atomic_store_rel_long(&myreq->lr_locked, 0);
+ }
}
}
diff --git a/lib/libkse/sys/lock.h b/lib/libkse/sys/lock.h
index 449ad36..e397111 100644
--- a/lib/libkse/sys/lock.h
+++ b/lib/libkse/sys/lock.h
@@ -55,6 +55,7 @@ struct lockreq {
struct lockuser *lr_watcher; /* only used for priority locks */
struct lockuser *lr_owner; /* only used for priority locks */
long lr_waiting; /* non-zero when wakeup needed */
+ volatile long lr_handshake; /* non-zero when wakeup in progress */
};
struct lockuser {
diff --git a/lib/libpthread/sys/lock.c b/lib/libpthread/sys/lock.c
index c77dfcb..12ce1a0 100644
--- a/lib/libpthread/sys/lock.c
+++ b/lib/libpthread/sys/lock.c
@@ -65,6 +65,7 @@ _lock_init(struct lock *lck, enum lock_type ltype,
lck->l_head->lr_watcher = NULL;
lck->l_head->lr_owner = NULL;
lck->l_head->lr_waiting = 0;
+ lck->l_head->lr_handshake = 0;
lck->l_tail = lck->l_head;
}
return (0);
@@ -84,6 +85,7 @@ _lockuser_init(struct lockuser *lu, void *priv)
lu->lu_myreq->lr_watcher = NULL;
lu->lu_myreq->lr_owner = lu;
lu->lu_myreq->lr_waiting = 0;
+ lu->lu_myreq->lr_handshake = 0;
lu->lu_watchreq = NULL;
lu->lu_priority = 0;
lu->lu_private = priv;
@@ -169,6 +171,12 @@ _lock_acquire(struct lock *lck, struct lockuser *lu, int prio)
while (lu->lu_watchreq->lr_locked != 0)
lck->l_wait(lck, lu);
atomic_store_rel_long(&lu->lu_watchreq->lr_waiting, 0);
+ /*
+ * Wait for original owner to stop accessing the
+ * lockreq object.
+ */
+ while (lu->lu_watchreq->lr_handshake)
+ ;
}
}
}
@@ -232,19 +240,24 @@ _lock_release(struct lock *lck, struct lockuser *lu)
}
}
if (lu_h != NULL) {
+ lu_h->lu_watchreq->lr_handshake = 1;
/* Give the lock to the highest priority user. */
atomic_store_rel_long(&lu_h->lu_watchreq->lr_locked, 0);
if ((lu_h->lu_watchreq->lr_waiting != 0) &&
(lck->l_wakeup != NULL))
/* Notify the sleeper */
lck->l_wakeup(lck, lu_h->lu_myreq->lr_watcher);
+ atomic_store_rel_long(&lu_h->lu_watchreq->lr_handshake,
+ 0);
} else {
+ myreq->lr_handshake = 1;
/* Give the lock to the previous request. */
atomic_store_rel_long(&myreq->lr_locked, 0);
if ((myreq->lr_waiting != 0) &&
(lck->l_wakeup != NULL))
/* Notify the sleeper */
lck->l_wakeup(lck, myreq->lr_watcher);
+ atomic_store_rel_long(&myreq->lr_handshake, 0);
}
} else {
/*
@@ -257,12 +270,19 @@ _lock_release(struct lock *lck, struct lockuser *lu)
lu->lu_watchreq = NULL;
lu->lu_myreq->lr_locked = 1;
lu->lu_myreq->lr_waiting = 0;
- /* Give the lock to the previous request. */
- atomic_store_rel_long(&myreq->lr_locked, 0);
- if ((myreq->lr_waiting != 0) &&
- (lck->l_wakeup != NULL))
- /* Notify the sleeper */
- lck->l_wakeup(lck, myreq->lr_watcher);
-
+ if (lck->l_wakeup) {
+ /* Start wakeup */
+ myreq->lr_handshake = 1;
+ /* Give the lock to the previous request. */
+ atomic_store_rel_long(&myreq->lr_locked, 0);
+ if (myreq->lr_waiting != 0) {
+ /* Notify the sleeper */
+ lck->l_wakeup(lck, myreq->lr_watcher);
+ }
+ /* Stop wakeup */
+ atomic_store_rel_long(&myreq->lr_handshake, 0);
+ } else {
+ atomic_store_rel_long(&myreq->lr_locked, 0);
+ }
}
}
diff --git a/lib/libpthread/sys/lock.h b/lib/libpthread/sys/lock.h
index 449ad36..e397111 100644
--- a/lib/libpthread/sys/lock.h
+++ b/lib/libpthread/sys/lock.h
@@ -55,6 +55,7 @@ struct lockreq {
struct lockuser *lr_watcher; /* only used for priority locks */
struct lockuser *lr_owner; /* only used for priority locks */
long lr_waiting; /* non-zero when wakeup needed */
+ volatile long lr_handshake; /* non-zero when wakeup in progress */
};
struct lockuser {
OpenPOWER on IntegriCloud