lwIP sockets: a failed listen or connect leaves the socket lock held, a later close hangs forever

Open Beginner friendly
#2,475 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
84/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
c, erlang
Domain
embedded-iot

Research direction

Start in src/libAtomVM/otp_socket.c and read nif_socket_listen and the lwIP branch of nif_socket_connect. Check how #2345 handles the socket lock on the tcp_close error path, then reproduce the failed listen or connect on an SMP RP2 build. Done when each error path releases the read lock and socket:close returns after the failure.

Written by the indexing model from the issue text.

Description

Affected: release-0.7 at ca1583068, lwIP socket backend (RP2 port), SMP builds
(the default on RP2). Not changed by #2345.

What happens

nif_socket_listen and nif_socket_connect take the socket read lock, then
return on an lwIP error without releasing it:

  • nif_socket_listen: if (new_pcb == NULL) { return make_lwip_err_tuple(err, ctx); }
  • nif_socket_connect (lwIP branch): if (err != ERR_OK) { ... return {error, closed} / RAISE_ERROR(...) }

The read lock is never released. Every later operation that takes the write lock
on that socket blocks forever and pins a scheduler thread: socket:close/1,
socket:recv/1,2,3, socket:accept/1,2, socket:send/2 (through the select
NIFs) and the monitor callback socket_down.

How to reproduce (Pico W)

Connect, with the network down or no route to the address (tcp_connect fails
with ERR_RTE):

{ok, S} = socket:open(inet, stream, tcp),
{error, closed} = socket:connect(S, #{family => inet, addr => {192, 168, 0, 1}, port => 80}),
ok = socket:close(S).   % never returns

Listen on a socket that is already connected (tcp_listen fails with ERR_CLSD):

{error, _} = socket:listen(ConnectedSocket),
ok = socket:close(ConnectedSocket).   % never returns

Where

src/libAtomVM/otp_socket.c, nif_socket_listen and nif_socket_connect,
#elif OTP_SOCKET_LWIP branches.

Suggested fix

SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock) before returning on both error
paths, as #2345 did for the failing tcp_close in nif_socket_close.

Dominant language
Erlang
Stars
2.3k
Forks
171
Avg merge
4d 11h
Merged PRs (30d)
20

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from atomvm/AtomVM

All issues in atomvm/AtomVM

Similar issues

More Embedded & IoT issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.