← board

PAL esp/lwIP: getsockname & recvfrom return an unfilled (zero) sockaddr

RESOLUTION (2026-06-22): not a PAL bug

Root-caused on-target to a riscv32 compiler bug, filed as feature-riscv32-var-param-forwarding: forwarding a var parameter into a nested routine's var parameter drops the write on riscv32. PalGetSockNameIpv4 / PalRecvFromIpv4 call the shared ParseSockAddrIpv4(@sa, outAddr, outPort) with the enclosing function's var params, so the parsed address never reaches the caller. lwIP fills the sockaddr correctly (proven inline). The PAL code is correct — no change here; this resolves when the compiler bug is fixed. The net-c3 smoke's address read-back checks re-enable then. The analysis below is kept for the trail.

Problem

On the ESP-IDF lwIP backend, the address read-back PAL calls return an all-zero sockaddr even though the socket operations themselves succeed:

The send-side sockaddr is correct: after fixing FillSockAddrIpv4 to the lwIP/BSD layout (sin_len@0, sin_family@1 — vs the Linux 2-byte family@0 the POSIX backend uses) the loopback datagram is delivered and PalPoll reports it readable. So bind / sendto / poll / recvfrom-payload all work; only the out-sockaddr fill is wrong.

The POSIX backend fills these correctly (host lib_platform_net_udp peer=ok, lib_platform_net_sockopt name/accept-peer=ok), so it is ESP/lwIP-specific.

Reproduce

cd examples/esp32/net-c3
. ~/esp/esp-idf/export.sh
./build.sh qemu      # boots esp32c3 under qemu-system-riscv32

The smoke prints PXX-net-smoke status=0 (core path) plus diagnostics bound-port=0 / peer-port=0 showing the read-back gap.

Root-cause analysis (static, 2026-06-22)

Read the IDF lwIP source — the layout/parse side is ruled out:

So an all-zero output means lwIP copied 0 bytes, i.e. it read *namelen == 0 on entry — even though the PAL sets addrlen := 16 before the call.

Prime suspect: riscv32 codegen of @<scalar-local> passed to an external. Note the asymmetry in the same call: @sa[0] (address of a local array element, the from buffer) reaches lwIP correctly — the payload is received — but @addrlen (address of a local scalar Integer) appears to arrive as a pointer to 0 / wrong slot. If confirmed this is a Track A riscv32 compiler bug, not an esp PAL defect, and the PAL code stays as-is (clean scalar @).

Cheap discriminator (one qemu cycle, throwaway — do NOT commit a workaround): in an isolated esp program call lwip_getsockname with addrlen as a scalar local vs as array[0..0] of Integer element. If the array form fills correctly and the scalar does not, file the riscv32 @scalar-local compiler bug and keep the PAL on the clean scalar form (blocked on the compiler fix). If both fail, the cause is lwIP/config and stays here.

All qemu-reproducible (no hardware) via the net-c3 smoke.

Acceptance

Log

CLOSED via triage (2026-06-30)

Root cause was NOT a PAL bug but the riscv32 var-param-forwarding compiler bug, now FIXED (commit f67fad2, in done/; esp32c3+s3 oracle-diff green). The PAL sockaddr read-back code is correct. Only remnant: re-enable the net-c3 smoke's ungated address-readback 'diagnostic' (main.pas ~18-19) under qemu — a trivial test re-enable, not a compiler gap. Resolved; closing.