@@ -17,6 +17,7 @@ void ip_thread_start(void);
1717#include <futex.h>
1818#include <locks.h>
1919#include <stdatomic.h>
20+ #include <unwind.h>
2021
2122/**
2223 * Flag used to synchronize the network stack thread and user threads at
@@ -37,11 +38,16 @@ static uint32_t threadEntryGuard;
3738uint16_t networkThreadID ;
3839
3940/**
40- * Global lock acquired by the IP thread at startup time. This lock is never
41- * released and acquiring it after startup will always fail. This lock can be
42- * used to force the IP thread to run for a short amount of time, e.g., when
43- * the internal FreeRTOS message queue is full and we want to let the IP thread
44- * empty it to close a socket.
41+ * Global lock acquired by the IP thread at startup time. In normal running
42+ * conditions, this lock is never released and acquiring it after startup will
43+ * always fail. This lock can be used to force the IP thread to run for a short
44+ * amount of time, e.g., when the internal FreeRTOS message queue is full and
45+ * we want to let the IP thread empty it to close a socket. See such an example
46+ * in `close_socket_retry`.
47+ *
48+ * Corner case: during a network stack reset, the lock is shortly released to
49+ * notify other threads that the IP thread is restarting. See comment in
50+ * `ip_thread_entry` for more details.
4551 */
4652struct FlagLockState ipThreadLockState ;
4753
@@ -106,6 +112,8 @@ void ip_cleanup(void)
106112 // modifiable by a potentially compromised compartment.
107113}
108114
115+ extern void reset_network_stack_state (bool isIpThread );
116+
109117void __cheri_compartment ("TCPIP" ) ip_thread_entry (void )
110118{
111119 FreeRTOS_printf (("ip_thread_entry\n" ));
@@ -114,36 +122,53 @@ void __cheri_compartment("TCPIP") ip_thread_entry(void)
114122
115123 while (1 )
116124 {
117- while ( threadEntryGuard == 0 )
125+ CHERIOT_DURING
118126 {
127+ while (threadEntryGuard == 0 )
128+ {
129+ FreeRTOS_printf (
130+ ("Sleeping until the IP task is supposed to start\n" ));
131+ futex_wait (& threadEntryGuard , 0 );
132+ }
133+
134+ // Reset the guard now: we will only ever re-iterate
135+ // the loop in the case of a network stack reset, in
136+ // which case we want to wait again for a call to
137+ // `ip_thread_start`.
138+ //
139+ // Note that we cannot reset this in `ip_cleanup`,
140+ // because that would overwrite the 1 written by
141+ // `ip_thread_start` if the latter was called before we
142+ // call `ip_cleanup`. This will happen if the IP thread
143+ // crashes, in which case we would first call
144+ // `ip_thread_start` in the error handler, and then
145+ // reset the context to `ip_thread_entry` (itself then
146+ // calling `ip_cleanup`).
147+ threadEntryGuard = 0 ;
148+
149+ xIPTaskHandle = networkThreadID ;
119150 FreeRTOS_printf (
120- ("Sleeping until the IP task is supposed to start\n" ));
121- futex_wait (& threadEntryGuard , 0 );
122- }
151+ ("ip_thread_entry starting, thread ID is %p\n" , xIPTaskHandle ));
123152
124- // Reset the guard now: we will only ever re-enter this
125- // function in the case of a network stack reset, in which case
126- // we want to wait again for a call to `ip_thread_start`.
127- //
128- // Note that we cannot reset this in `ip_cleanup`, because that
129- // would overwrite the 1 written by `ip_thread_start` if the
130- // latter was called before we call `ip_cleanup`. This will
131- // happen if the IP thread crashes, in which case we would
132- // first call `ip_thread_start` in the error handler, and then
133- // reset the context to `ip_thread_entry` (itself then calling
134- // `ip_cleanup`).
135- threadEntryGuard = 0 ;
136-
137- xIPTaskHandle = networkThreadID ;
138- FreeRTOS_printf (
139- ("ip_thread_entry starting, thread ID is %p\n" , xIPTaskHandle ));
140-
141- flaglock_priority_inheriting_lock (& ipThreadLockState );
142-
143- // FreeRTOS event loop. This will only return if a user thread
144- // crashed.
145- prvIPTask (NULL );
153+ flaglock_priority_inheriting_lock (& ipThreadLockState );
146154
155+ // FreeRTOS event loop. This will only return if a user
156+ // thread crashed.
157+ prvIPTask (NULL );
158+ }
159+ CHERIOT_HANDLER
160+ {
161+ // Call the network stack error handler for the network thread.
162+ reset_network_stack_state (true /* this is the IP thread */ );
163+ }
164+ CHERIOT_END_HANDLER
165+
166+ // Release the IP thread lock. We will re-acquire it in the
167+ // next loop iteration. This is necessary if a user thread
168+ // triggered a reset. In such a case, the user thread error
169+ // handler will wait for the IP thread to restart by acquiring
170+ // the `ipThreadLockState` with an infinite timeout (and
171+ // immediately releasing it thereafter).
147172 flaglock_unlock (& ipThreadLockState );
148173 }
149174}
0 commit comments