Skip to content

Commit 8ac3a43

Browse files
committed
pkg/lwip: fix race in async sock API with event
In TCP server mode, the sock_tcp_t sockets are managed by the network stack and can be reused if a previous connection is no longer in used. However, an event may still be posted in the event queue when the socket is reused. Wiping it will result in the `next` pointer in that event to be NULL, which will cause the event handler fetching that event to crash. This adds an `event_cancel()` at two places: 1. Just before reusing the socket 2. During sock_tcp_disconnect() The former catches issues in server mode e.g. when a connect has been closed (e.g. due to timeout) and is reused before a pending event (e.g. a timeout event) has been processed. The letter may be an issue on client side. E.g. when `sock_tcp_t` was allocated on stack and goes out of scope after `sock_tcp_disconnect` but before the event handler was run.
1 parent 4044c85 commit 8ac3a43

1 file changed

Lines changed: 21 additions & 0 deletions

File tree

pkg/lwip/contrib/sock/tcp/lwip_sock_tcp.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ int sock_tcp_listen(sock_tcp_queue_t *queue, const sock_tcp_ep_t *local,
8585
queue->array = queue_array;
8686
queue->len = queue_len;
8787
queue->used = 0;
88+
/* This wipe is important: We cancel pending events in async event API when
89+
* reusing the socket. If the memory would be uninitialized, bad things
90+
* would happen */
8891
memset(queue->array, 0, sizeof(sock_tcp_t) * queue_len);
8992
mutex_unlock(&queue->mutex);
9093

@@ -125,6 +128,14 @@ void sock_tcp_disconnect(sock_tcp_t *sock)
125128
}
126129
}
127130

131+
#ifdef SOCK_HAS_ASYNC_CTX
132+
/* Cancel any pending event in the event queue */
133+
if (sock->base.async_ctx.queue) {
134+
event_cancel(sock->base.async_ctx.queue,
135+
&sock->base.async_ctx.event.super);
136+
}
137+
#endif
138+
128139
mutex_unlock(&sock->mutex);
129140
memset(&sock->mutex, 0, sizeof(mutex_t));
130141
}
@@ -240,6 +251,16 @@ int sock_tcp_accept(sock_tcp_queue_t *queue, sock_tcp_t **sock,
240251
for (unsigned short i = 0; i < queue->len; i++) {
241252
sock_tcp_t *s = &queue->array[i];
242253
if (s->base.conn == NULL) {
254+
#ifdef SOCK_HAS_ASYNC_CTX
255+
/* If there still is an event pending, we cannot just wipe
256+
* its memory but have to remove the event from the list
257+
* first. We rely here sock_tcp_listen to zero-initialize
258+
* the sockets. */
259+
if (s->base.async_ctx.queue) {
260+
event_cancel(s->base.async_ctx.queue,
261+
&s->base.async_ctx.event.super);
262+
}
263+
#endif
243264
_tcp_sock_init(s, tmp, queue);
244265
queue->used++;
245266
assert(queue->used > 0);

0 commit comments

Comments
 (0)