Skip to content

extmod/modlwip: Fix getaddrinfo blocking indefinitely without network. - #18801

Open
paul-matthews wants to merge 1 commit into
micropython:masterfrom
paul-matthews:fix-getaddrinfo-timeout
Open

paul-matthews wants to merge 1 commit into
micropython:masterfrom
paul-matthews:fix-getaddrinfo-timeout

Conversation

@paul-matthews

Copy link
Copy Markdown

Summary

socket.getaddrinfo() blocks indefinitely on lwIP-based ports (confirmed on rp2/CYW43) when WiFi was previously connected then disconnected. This adds two fixes to the ERR_INPROGRESS case in lwip_getaddrinfo():

  • Early-fail check: Before polling, verify at least one netif is up, has link, and has a valid IP. If not, fail immediately with -2 (consistent with lwip_getaddrinfo_cb failure). This catches WiFi-inactive, active-but-never-connected, and disconnected-after-connection scenarios.
  • Safety-net timeout (20s): Guards against any other scenario where lwIP's internal DNS timeout doesn't fire.

Root Cause

Two factors combine to cause the indefinite hang:

  1. Cached DNS servers: After WiFi disconnect, DNS server addresses from the previous DHCP lease remain cached in lwIP. dns_gethostbyname() enqueues a query (returning ERR_INPROGRESS) even though no network path exists.

  2. Soft timer self-disables: On the rp2 port, the lwIP soft timer switches to ONE_SHOT mode when no netif has LINK_UP (ports/rp2/mpnetworkport.c:150-161). Without the periodic timer, dns_tmr() never fires, so lwIP's built-in DNS retry/timeout mechanism is dead. The polling loop spins forever.

The early-fail check must verify three conditions (not just IP):

  • netif_is_up() — interface administratively up
  • netif_is_link_up() — physical link up (WiFi associated)
  • !ip4_addr_isany_val() — valid IP (DHCP complete)

After disconnect, LINK_UP is cleared but the DHCP IP address lingers, so checking IP alone is insufficient.

Test Results

Tested on Raspberry Pi Pico 2 W (RP2350 + CYW43439), MicroPython v1.26.1:

# WiFi State Expected Result
1 IP literal, WiFi off Resolves (ERR_OK path) [(2,1,0,'',('10.0.0.1',80))] in 0ms
2 Connected DNS resolves 142.251.30.103 in 23ms
3 Was connected, then disconnect() OSError(-2) immediate OSError(-2) in 0ms
4 active(True), never connected OSError(-2) immediate OSError(-2) in 1ms
5 active(False) OSError(-2) immediate OSError(-2) in 1ms

All 5/5 pass. Without the fix, tests 3 and 4 hang indefinitely.

Notes

  • The fix is in extmod/modlwip.c which is shared across all lwIP ports (rp2, esp32, etc.)
  • IP literal resolution and cached DNS results take the ERR_OK path and bypass this code entirely
  • The -2 status value is consistent with the existing lwip_getaddrinfo_cb() failure convention

Fixes #18797

When WiFi was previously connected then disconnected,
socket.getaddrinfo() blocks indefinitely on ports using the lwIP stack
(confirmed on rp2/CYW43).  Two factors cause this:

1. Cached DNS server addresses from the previous DHCP lease cause lwIP's
   dns_gethostbyname() to enqueue a query (returning ERR_INPROGRESS)
   even though no network path exists.

2. On some ports (e.g. rp2), the lwIP soft timer self-disables when no
   netif has LINK_UP (see ports/rp2/mpnetworkport.c:150-161), preventing
   dns_tmr() from firing.  Without that timer, lwIP's built-in DNS
   retry/timeout mechanism never triggers, so the polling loop in
   getaddrinfo spins forever.

This commit adds two fixes to the ERR_INPROGRESS case:

- Early-fail: before entering the polling loop, check that at least one
  netif is administratively up, has physical link, and has a valid IP.
  If not, fail immediately with status -2 (same error code used by
  lwip_getaddrinfo_cb for DNS failure).

- Safety-net timeout: if the polling loop runs for more than 20 seconds
  without resolution, break out and fail.  This guards against any other
  scenario where lwIP's internal DNS timeout doesn't fire.

The early-fail check uses netif_is_up(), netif_is_link_up(), and
ip4_addr_isany_val() to cover all disconnection scenarios:
- WiFi inactive: netif not up
- WiFi active, never connected: IP is 0.0.0.0
- WiFi was connected, then disconnected: LINK_UP cleared (IP may linger)

Tested on Raspberry Pi Pico 2 W (RP2350 + CYW43) with 5 scenarios:
1. WiFi inactive → OSError(-2) in 0ms
2. WiFi connected → DNS resolves normally (23ms)
3. WiFi disconnected after connection → OSError(-2) in 0ms
4. WiFi active, never connected → OSError(-2) in 1ms
5. IP literal with WiFi off → resolves in 0ms (ERR_OK path, unaffected)

Fixes micropython#18797.

Signed-off-by: Paul Matthews <[email protected]>
@codecov

codecov Bot commented Feb 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.42%. Comparing base (588fa2b) to head (b181487).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #18801   +/-   ##
=======================================
  Coverage   98.42%   98.42%           
=======================================
  Files         174      174           
  Lines       22333    22333           
=======================================
  Hits        21982    21982           
  Misses        351      351           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown

Code size report:

Reference:  extmod/modlwip: Always set the lwip_socket_ioctl() return value. [588fa2b]
Comparison: extmod/modlwip: Fix getaddrinfo blocking indefinitely without network. [merge of b181487]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:    +0 +0.000% TEENSY40
        rp2:   +64 +0.007% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@dpgeorge dpgeorge added the extmod Relates to extmod/ directory in source label Feb 10, 2026
@projectgus
projectgus self-requested a review February 10, 2026 23:27
@projectgus
projectgus removed their request for review February 17, 2026 23:13
@dpgeorge

dpgeorge commented Mar 4, 2026

Copy link
Copy Markdown
Member

Thanks for making this PR and adding comprehensive test results.

As discussed, it was fixed differently in #18805 / bfc69db.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extmod Relates to extmod/ directory in source

Projects

None yet

Development

Successfully merging this pull request may close these issues.

socket.getaddrinfo() blocks indefinitely when WiFi active but not connected (RP2350/CYW43)

2 participants