zephyr/subsys/net/lib/sockets
Nicolas Pitre cf49699b0d net: sockets: socketpair: fix locking
The irq_lock() usage here is incompatible with SMP systems, and one's
first reaction might be to convert it to a spinlock.

But are those irq_lock() instances really necessary?

Commit 6161ea2542 ("net: socket: socketpair: mitigate possible race
condition") doesn't say much:

> There was a possible race condition between sock_is_nonblock()
> and k_sem_take() in spair_read() and spair_write() that was
> mitigated.

A possible race without the irq_lock would be:

thread A                thread B
|                       |
+ spair_write():        |
+   is_nonblock = sock_is_nonblock(spair); [false]
*   [preemption here]   |
|                       + spair_ioctl():
|                       +   res = k_sem_take(&spair->sem, K_FOREVER);
|                       +   [...]
|                       +   spair->flags |= SPAIR_FLAG_NONBLOCK;
|                       *   [preemption here]
+   res = k_sem_take(&spair->sem, K_NO_WAIT); [-1]
+   if (res < 0) {      |
+     if (is_nonblock) { [skipped] }
*     res = k_sem_take(&spair->sem, K_FOREVER); [blocks here]
|                       +   [...]

But the version with irq_lock() isn't much better:

thread A                thread B
|                       |
|                       + spair_ioctl():
|                       +   res = k_sem_take(&spair->sem, K_FOREVER);
|                       +   [...]
|                       *   [preemption here]
+ spair_write():        |
+   irq_lock();         |
+   is_nonblock = sock_is_nonblock(spair); [false]
+   res = k_sem_take(&spair->sem, K_NO_WAIT); [-1]
+   irq_unlock();       |
*   [preemption here]   |
|                       +   spair->flags |= SPAIR_FLAG_NONBLOCK;
|                       +   [...]
|                       +   k_sem_give(&spair->sem);
|                       + spair_read():
|                       +   res = k_sem_take(&spair->sem, K_NO_WAIT);
|                       *   [preemption here]
+   if (res < 0) {      |
+     if (is_nonblock) { [skipped] }
*     res = k_sem_take(&spair->sem, K_FOREVER); [blocks here]

In both cases the last k_sem_take(K_FOREVER) will block despite
SPAIR_FLAG_NONBLOCK being set at that moment. Other race scenarios
exist too, and on SMP they are even more likely.

The only guarantee provided by the irq_lock() is to make sure that
whenever the semaphore is acquired then the is_nonblock value is always
current. A better way to achieve that and be SMP compatible is to simply
move the initial sock_is_nonblock() *after* the k_sem_take() and remove
those irq_locks().

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2021-10-11 21:00:41 -04:00
..
CMakeLists.txt net: socket: packet: using pckt sckt for passing the PPP dialup data 2021-04-01 09:43:56 +03:00
getaddrinfo.c net: dns: Fix multiple IP DNS resolution 2021-06-07 23:54:55 -04:00
getnameinfo.c
Kconfig net: sockets: Add socket processing priority 2021-09-28 20:11:26 -04:00
socket_offload.c
socketpair.c net: sockets: socketpair: fix locking 2021-10-11 21:00:41 -04:00
sockets_can.c net: sockets: sockets_can: Allow parallel receive/send 2021-10-06 22:22:43 -04:00
sockets_internal.h net: sockets: sockets_can: Allow parallel receive/send 2021-10-06 22:22:43 -04:00
sockets_misc.c
sockets_net_mgmt.c net: sockets: Add socket processing priority 2021-09-28 20:11:26 -04:00
sockets_packet.c net: sockets: Add socket processing priority 2021-09-28 20:11:26 -04:00
sockets_select.c net: socket: Allow microsecond accuracy in zsock_select() 2021-06-17 15:23:13 +03:00
sockets_tls.c net: sockets: tls: Use better error code 2021-10-07 14:02:40 -05:00
sockets.c net: sockets: sockets_can: Allow parallel receive/send 2021-10-06 22:22:43 -04:00