Repository navigation
extmod/modlwip: Fix getaddrinfo blocking indefinitely without network. - #18801
Open
paul-matthews wants to merge 1 commit into
Open
paul-matthews wants to merge 1 commit into
paul-matthews wants to merge 1 commit into
Conversation
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 Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
|
Code size report: |
projectgus
self-requested a review
February 10, 2026 23:27
projectgus
removed their request for review
February 17, 2026 23:13
Member
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 theERR_INPROGRESScase inlwip_getaddrinfo():-2(consistent withlwip_getaddrinfo_cbfailure). This catches WiFi-inactive, active-but-never-connected, and disconnected-after-connection scenarios.Root Cause
Two factors combine to cause the indefinite hang:
Cached DNS servers: After WiFi disconnect, DNS server addresses from the previous DHCP lease remain cached in lwIP.
dns_gethostbyname()enqueues a query (returningERR_INPROGRESS) even though no network path exists.Soft timer self-disables: On the rp2 port, the lwIP soft timer switches to
ONE_SHOTmode when no netif hasLINK_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 upnetif_is_link_up()— physical link up (WiFi associated)!ip4_addr_isany_val()— valid IP (DHCP complete)After disconnect,
LINK_UPis 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:
[(2,1,0,'',('10.0.0.1',80))]in 0ms142.251.30.103in 23msdisconnect()OSError(-2)immediateOSError(-2)in 0msactive(True), never connectedOSError(-2)immediateOSError(-2)in 1msactive(False)OSError(-2)immediateOSError(-2)in 1msAll 5/5 pass. Without the fix, tests 3 and 4 hang indefinitely.
Notes
extmod/modlwip.cwhich is shared across all lwIP ports (rp2, esp32, etc.)ERR_OKpath and bypass this code entirely-2status value is consistent with the existinglwip_getaddrinfo_cb()failure conventionFixes #18797