zephyr/subsys/net/lib
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
..
capture net: remove usage of device_pm_control_nop 2021-04-27 16:28:49 -04:00
coap net: coap: coap_next_token single sys_rand_get call 2021-07-29 11:47:51 -04:00
config net: config: Try to only use auto started interface 2021-03-29 07:40:24 -04:00
conn_mgr kernel: sem: add K_SEM_MAX_LIMIT 2021-03-05 08:13:53 -06:00
dns net: mdns + dns_sd: fix regression that breaks ptr queries 2021-10-11 08:50:45 -04:00
http net: http: Fix HTTP_DATA_FINAL notification 2021-09-17 08:12:00 -04:00
lwm2m net: lwm2m: Fix device obj missing error code ri 2021-10-07 15:44:46 -04:00
mqtt net: mqtt: Handle incomplete zsock_sendmsg write 2021-09-29 11:08:40 +02:00
openthread openthread: align platform code to ARM PSA changes 2021-10-01 11:37:38 +02:00
sntp net: sntp: Depend on NET_SOCKETS_POSIX_NAMES || POSIX_API 2021-04-22 13:41:33 +03:00
sockets net: sockets: socketpair: fix locking 2021-10-11 21:00:41 -04:00
socks
tftp net: refactor tftp library 2021-06-29 09:15:55 -04:00
tls_credentials
utils
websocket net: websocket: s/mbedtls_sha1_ret/mbedtls_sha1/ 2021-10-07 14:02:40 -05:00
CMakeLists.txt net: capture: Add support for network packet capturing 2021-04-02 07:24:06 -04:00
Kconfig net: capture: Add support for network packet capturing 2021-04-02 07:24:06 -04:00