Skip to content

Commit 39de80a

Browse files
committed
Make waiting behavior use steps in event groups.
Event groups are used by the TCP/IP stack. Unfortunately, if a thread somehow corrupts the socket list, we will loose the ability to retrieve references to event groups, ultimately preventing us from unblocking threads blocked on event group locks and futexes. Fortunately, the TCP/IP stack can heap-free-all event groups. Thus we need any wait done on event group locks and futexes to be performed in steps (see recent additions to the `LockGuard` class) to ensure that blocking threads will crash within a bounded amount of time when a reset is triggered. Signed-off-by: Hugo Lefeuvre <hugo.lefeuvre@ubc.ca>
1 parent bb40b44 commit 39de80a

File tree

1 file changed

+33
-11
lines changed

1 file changed

+33
-11
lines changed

sdk/lib/event_group/event_group.cc

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88

99
using Debug = ConditionalDebug<false, "Event groups library">;
1010

11+
/**
12+
* Length of a locking step for even group locks and futex words. When we
13+
* attempt to grab an event group lock or wait on a futex, do so in steps of
14+
* `EventGroupLockTimeoutStep`. That way, if the user of the event group
15+
* crashes and we somehow do not have a pointer to the event group lock anymore
16+
* (and thus cannot reset it), we have a guarantee that the blocking thread
17+
* will become live again within a bounded amount of time, and crash as it is
18+
* trying to re-acquire the lock if we free it through heap-free-all.
19+
*/
20+
static constexpr const Ticks EventGroupLockTimeoutStep = MS_TO_TICKS(500);
21+
1122
struct EventWaiter
1223
{
1324
std::atomic<uint32_t> bitsSeen;
@@ -74,7 +85,7 @@ int eventgroup_wait(Timeout *timeout,
7485
auto &waiter = group->waiters[thread_id_get()];
7586
uint32_t bitsSeen;
7687
// Set up our state for the waiter with the lock held.
77-
if (LockGuard g{group->lock, timeout})
88+
if (LockGuard g{group->lock, timeout, EventGroupLockTimeoutStep})
7889
{
7990
bitsSeen = group->bits;
8091
// If the condition holds, return immediately
@@ -98,15 +109,23 @@ int eventgroup_wait(Timeout *timeout,
98109
}
99110
// If the condition didn't hold, wait for
100111
Debug::log("Waiting on futex {} ({})", &waiter.bitsSeen, bitsSeen);
101-
while (waiter.bitsSeen.wait(timeout, bitsSeen) != -ETIMEDOUT)
112+
// Wait on the futex word in steps of `EventGroupLockTimeoutStep`
113+
// ticks. See documentation for `EventGroupLockTimeoutStep` above.
114+
do
102115
{
103-
bitsSeen = waiter.bitsSeen.load();
104-
if (isTriggered(bitsSeen))
116+
Timeout t{std::min(EventGroupLockTimeoutStep, timeout->remaining)};
117+
while (waiter.bitsSeen.wait(&t, bitsSeen) != -ETIMEDOUT)
105118
{
106-
*outBits = bitsSeen;
107-
return 0;
108-
}
109-
};
119+
bitsSeen = waiter.bitsSeen.load();
120+
if (isTriggered(bitsSeen))
121+
{
122+
timeout->elapse(t.elapsed);
123+
*outBits = bitsSeen;
124+
return 0;
125+
}
126+
};
127+
timeout->elapse(t.elapsed);
128+
} while (timeout->may_block());
110129
waiter.bitsWanted = 0;
111130
*outBits = group->bits;
112131
return -ETIMEDOUT;
@@ -117,7 +136,7 @@ int eventgroup_clear(Timeout *timeout,
117136
uint32_t *outBits,
118137
uint32_t bitsToClear)
119138
{
120-
if (LockGuard g{group->lock, timeout})
139+
if (LockGuard g{group->lock, timeout, EventGroupLockTimeoutStep})
121140
{
122141
Debug::log(
123142
"Bits was {}, clearing with mask {}", group->bits, ~bitsToClear);
@@ -134,7 +153,7 @@ int eventgroup_set(Timeout *timeout,
134153
uint32_t *outBits,
135154
uint32_t bitsToSet)
136155
{
137-
if (LockGuard g{group->lock, timeout})
156+
if (LockGuard g{group->lock, timeout, EventGroupLockTimeoutStep})
138157
{
139158
Debug::log("Bits was {}, setting {}", group->bits, bitsToSet);
140159
group->bits |= bitsToSet;
@@ -197,6 +216,9 @@ int eventgroup_destroy_force(SObjStruct *heapCapability, EventGroup *group)
197216

198217
int eventgroup_destroy(SObjStruct *heapCapability, EventGroup *group)
199218
{
200-
group->lock.lock();
219+
Timeout unlimited{UnlimitedTimeout};
220+
LockGuard g{group->lock, &unlimited, EventGroupLockTimeoutStep};
221+
// The lock will be freed as part of `eventgroup_destroy_force`.
222+
g.release();
201223
return eventgroup_destroy_force(heapCapability, group);
202224
}

0 commit comments

Comments
 (0)