Describe the bug
can_close_socket() in subsys/net/lib/sockets/sockets_can.c never calls net_context_put(). As a result, closing a SOCK_RAW/AF_CAN socket detaches the native CAN filter but never releases the underlying net_context object back to the contexts[CONFIG_NET_MAX_CONTEXTS] pool (subsys/net/ip/net_context.c). Every close() on a CAN socket permanently consumes one context slot. After CONFIG_NET_MAX_CONTEXTS (default 6) close+reopen cycles -- regardless of whether they happen on one socket or spread across many -- every subsequent socket()/bind() call for any CAN socket in the system fails with -ENOENT (-2), logged as:
net_conn: Not enough connection contexts. Consider increasing CONFIG_NET_MAX_CONN.
(That log message is misleading -- CONFIG_NET_MAX_CONN is a different, unrelated table in net_conn.c. The pool that is actually exhausted is CONFIG_NET_MAX_CONTEXTS, sized in net_context.c.)
I also found a second, related bug in the same function: the loop returns as soon as it finds the first receivers[] entry matching the closing ctx. A socket that has registered more than one filter (each setsockopt(CAN_RAW_FILTER) call adds a separate receivers[] entry for the same ctx -- this Zephyr version only accepts one filter per setsockopt() call) leaks every filter slot after the first one on close.
Verified against current upstream main (source fetched directly), not just an older pinned version -- this is present today, not something already fixed in a newer release.
Regression
Steps to reproduce
Reproduced on real hardware with the original, unmodified samples/net/sockets/can/src/main.c (only main() renamed to coexist with another main() in the same app, and a redundant can_start() call removed -- no other changes):
-
Build/flash the sample (or any app) with CONFIG_NET_SOCKETS_CAN_RECEIVERS raised enough to register 2 filters (default sample already creates a 2nd RX socket/thread when this Kconfig's value happens to be exactly 2; we forced that code path on via #if 1 so it runs regardless of the Kconfig's actual value).
-
In rx(), the 2nd RX thread already contains a CLOSE_PERIOD-driven close/reopen: every 15 received frames, it does close(fd); create_socket(...) (the sample's own existing test code, unmodified).
-
Send CAN frames matching the registered filter (ID 0x1, standard frame) from an external CAN tool (PCAN) at ~1 frame/sec so nothing gets lost to log-buffer congestion.
-
After 15 * CONFIG_NET_MAX_CONTEXTS frames (default 15 * 6 = 90; in practice we saw it fail after ~75-90 received frames / ~7-8 close/reopen cycles), the next create_socket() inside rx() fails:
[00:06:58.162,170] net_conn: Not enough connection contexts. Consider increasing CONFIG_NET_MAX_CONN.
[00:06:58.162,170] demo_leak_repro: Cannot bind 2nd CAN socket (-2)
[00:06:58.162,200] demo_leak_repro: Cannot get socket (-2)
After this, the 2nd RX thread hits its own return; on that error path and dies permanently -- no CAN socket can be created again on that iface for the remaining runtime of the device.
Confirmed at the net_context level via a debugger watch on the global contexts[] array in net_context.c: contexts[i].flags bit 0 (NET_CONTEXT_IN_USE) never clears after close() on a CAN socket -- every close/reopen cycle lights up a brand-new, previously-unused slot instead of reusing the one that was just closed, until all CONFIG_NET_MAX_CONTEXTS slots read IN_USE = 1 with none ever returning to 0.
Relevant log output
[00:06:58.162,170] <err> net_conn: Not enough connection contexts. Consider increasing CONFIG_NET_MAX_CONN.
[00:06:58.162,170] <err> demo_leak_repro: Cannot bind 2nd CAN socket (-2)
[00:06:58.162,200] <err> demo_leak_repro: Cannot get socket (-2)
Impact
Annoyance – Minor irritation; no significant impact on usability or functionality.
Environment
Zephyr version: v4.4.0-10048-gb04994206c69 (also independently verified the same missing call is present on current upstream main, subsys/net/lib/sockets/sockets_can.c)
Board: frdm_mcxw71 (NXP MCXW716C, FlexCAN peripheral) -- but this is generic Zephyr socket-layer code, not board/driver-specific; the bug is not tied to this board or the FlexCAN driver.
Build system: west, Zephyr SDK toolchain
Relevant prj.conf settings: CONFIG_NET_SOCKETS_CAN=y, CONFIG_NET_MAX_CONTEXTS at its Zephyr default (6, not overridden)
Additional Context
Traced the missing call back to the CAN-close feature's original 2019 commit (06b500b, "net: sockets: can: Close the socket cleanly") -- its own commit message describes only detaching the native CAN filter/stopping CAN interrupts; releasing the net_context itself was never in scope for that change, and no later commit has added it.
Local patch applied and confirmed on hardware to fix the issue (verified via the contexts[] debugger watch described above -- the closed slot's flags now correctly clears NET_CONTEXT_IN_USE and gets reused on the next socket() call instead of the pool being permanently drained):
static int can_close_socket(struct net_context *ctx)
{
int i, ret;
bool found = false;
for (i = 0; i < ARRAY_SIZE(receivers); i++) {
if (receivers[i].ctx == ctx) {
struct socketcan_filter sfilter;
receivers[i].ctx = NULL;
found = true;
sfilter.can_id = receivers[i].can_id;
sfilter.can_mask = receivers[i].can_mask;
if (!is_already_attached(&sfilter,
net_context_get_iface(ctx),
ctx)) {
/* We can detach now as there are no other
* sockets that have same filter.
*/
ret = close_socket(ctx);
if (ret < 0) {
return ret;
}
}
}
/* NOTE: original code returned here after the first match,
* leaking every receivers[] entry after the first for a
* socket registered with more than one filter. Removed the
* early return so all matching entries are cleared.
*/
}
if (found) {
/* Release the net_context back to the CONFIG_NET_MAX_CONTEXTS
* pool. Without this call the context stays marked
* NET_CONTEXT_IN_USE forever.
*/
net_context_put(ctx);
}
return 0;
}
Describe the bug
can_close_socket() in subsys/net/lib/sockets/sockets_can.c never calls net_context_put(). As a result, closing a SOCK_RAW/AF_CAN socket detaches the native CAN filter but never releases the underlying net_context object back to the contexts[CONFIG_NET_MAX_CONTEXTS] pool (subsys/net/ip/net_context.c). Every close() on a CAN socket permanently consumes one context slot. After CONFIG_NET_MAX_CONTEXTS (default 6) close+reopen cycles -- regardless of whether they happen on one socket or spread across many -- every subsequent socket()/bind() call for any CAN socket in the system fails with -ENOENT (-2), logged as:
net_conn: Not enough connection contexts. Consider increasing CONFIG_NET_MAX_CONN.
(That log message is misleading -- CONFIG_NET_MAX_CONN is a different, unrelated table in net_conn.c. The pool that is actually exhausted is CONFIG_NET_MAX_CONTEXTS, sized in net_context.c.)
I also found a second, related bug in the same function: the loop returns as soon as it finds the first receivers[] entry matching the closing ctx. A socket that has registered more than one filter (each setsockopt(CAN_RAW_FILTER) call adds a separate receivers[] entry for the same ctx -- this Zephyr version only accepts one filter per setsockopt() call) leaks every filter slot after the first one on close.
Verified against current upstream main (source fetched directly), not just an older pinned version -- this is present today, not something already fixed in a newer release.
Regression
Steps to reproduce
Reproduced on real hardware with the original, unmodified samples/net/sockets/can/src/main.c (only main() renamed to coexist with another main() in the same app, and a redundant can_start() call removed -- no other changes):
Build/flash the sample (or any app) with CONFIG_NET_SOCKETS_CAN_RECEIVERS raised enough to register 2 filters (default sample already creates a 2nd RX socket/thread when this Kconfig's value happens to be exactly 2; we forced that code path on via #if 1 so it runs regardless of the Kconfig's actual value).
In rx(), the 2nd RX thread already contains a CLOSE_PERIOD-driven close/reopen: every 15 received frames, it does close(fd); create_socket(...) (the sample's own existing test code, unmodified).
Send CAN frames matching the registered filter (ID 0x1, standard frame) from an external CAN tool (PCAN) at ~1 frame/sec so nothing gets lost to log-buffer congestion.
After 15 * CONFIG_NET_MAX_CONTEXTS frames (default 15 * 6 = 90; in practice we saw it fail after ~75-90 received frames / ~7-8 close/reopen cycles), the next create_socket() inside rx() fails:
[00:06:58.162,170] net_conn: Not enough connection contexts. Consider increasing CONFIG_NET_MAX_CONN.
[00:06:58.162,170] demo_leak_repro: Cannot bind 2nd CAN socket (-2)
[00:06:58.162,200] demo_leak_repro: Cannot get socket (-2)
After this, the 2nd RX thread hits its own return; on that error path and dies permanently -- no CAN socket can be created again on that iface for the remaining runtime of the device.
Confirmed at the net_context level via a debugger watch on the global contexts[] array in net_context.c: contexts[i].flags bit 0 (NET_CONTEXT_IN_USE) never clears after close() on a CAN socket -- every close/reopen cycle lights up a brand-new, previously-unused slot instead of reusing the one that was just closed, until all CONFIG_NET_MAX_CONTEXTS slots read IN_USE = 1 with none ever returning to 0.
Relevant log output
Impact
Annoyance – Minor irritation; no significant impact on usability or functionality.
Environment
Zephyr version: v4.4.0-10048-gb04994206c69 (also independently verified the same missing call is present on current upstream main, subsys/net/lib/sockets/sockets_can.c)
Board: frdm_mcxw71 (NXP MCXW716C, FlexCAN peripheral) -- but this is generic Zephyr socket-layer code, not board/driver-specific; the bug is not tied to this board or the FlexCAN driver.
Build system: west, Zephyr SDK toolchain
Relevant prj.conf settings: CONFIG_NET_SOCKETS_CAN=y, CONFIG_NET_MAX_CONTEXTS at its Zephyr default (6, not overridden)
Additional Context
Traced the missing call back to the CAN-close feature's original 2019 commit (06b500b, "net: sockets: can: Close the socket cleanly") -- its own commit message describes only detaching the native CAN filter/stopping CAN interrupts; releasing the net_context itself was never in scope for that change, and no later commit has added it.
Local patch applied and confirmed on hardware to fix the issue (verified via the contexts[] debugger watch described above -- the closed slot's flags now correctly clears NET_CONTEXT_IN_USE and gets reused on the next socket() call instead of the pool being permanently drained):