8000 Wiznet5K ethernet + adafruit_requests using SSL causes hard crash with a "pystack exhausted" runtime error · Issue #10345 · adafruit/circuitpython · GitHub
[go: up one dir, main page]

Skip to content
Wiznet5K ethernet + adafruit_requests using SSL causes hard crash with a "pystack exhausted" runtime error #10345
Closed
@dsmith9000

Description

@dsmith9000

CircuitPython version and board name

Circuitpython:
Version 9.2.7

Hardware:
1) Adafruit ESP32-S3 feather (4MB FLASH / 2MB PSRAM) + Adafruit Ethernet Featherwing
2) RP2040 PicoW + Adafruit Ethernet Featherwing (with minor modifications to the example code to address the differences between this platform and the ESP32-S3 platform)

Code/REPL

# Experiment with ethernet/ssl/tls and Circuitpython.
# Hardware: Adafruit ESP32-S3 4MB FLASH/2MB PSRAM + Adafruit Ethernet Featherwing
# Circuitpython v9.2.7
# 4/14/2025

import adafruit_connection_manager
import adafruit_requests
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
from adafruit_wiznet5k.adafruit_wiznet5k_socketpool import SocketPool
from adafruit_wiznet5k.adafruit_wiznet5k_dns import DNS
import array
import board
import busio
import digitalio
import gc
import ssl
import sys


# Connection method options
METHOD_REQUESTS = "requests"        # Use 'requests' to make connection
METHOD_SOCKETS = "sockets"          # Use 'sockets' to make connection

# Select method to use
method_to_use = METHOD_REQUESTS


# Configuration
W5500_CS_PIN = board.D10



def print_ipaddress(ip_address: bytes|tuple(int)):
    addy = [str(octet) for octet in ip_address]
    print(".".join(addy))


def print_ifconfig(ifconfig):
    print("ifconfig: ", end="")
    for ip in ifconfig:
        print(".".join(f"{byte}" for byte in ip), " ", end="")
    print("\n")



def connect_using_https_with_requests(eth):
    print("<<HTTPS USING REQUESTS>>")
        
    # Initialize a requests session
    pool = adafruit_connection_manager.get_radio_socketpool(eth)
    ssl_context = adafruit_connection_manager.get_radio_ssl_context(eth)
    requests = adafruit_requests.Session(pool, ssl_context)

    gc.collect()
    try:
        r = requests.get("https://www.google.com", stream=True)     # RuntimeError('pystack exhausted') occurs here

    except RuntimeError as ex:
        print("Error :", repr(ex))
        sys.exit()
    
    print("Bytes received: ", len(r.text))
    print("First 100 bytes of response:\n", r.text[:100])
    sys.exit()


def connect_using_https_with_sockets(eth):
    print("<<HTTPS USING SECURE SOCKETS>>")
    hostname = "www.google.com"
    buffer = array.array("B", [0]*1024)

    # Initialize a requests session
    pool = adafruit_connection_manager.get_radio_socketpool(eth)
    ssl_context = ssl.create_default_context()

    s = pool.socket(type=SocketPool.SOCK_STREAM)
    
    # Create secure socket (ssl/tls)
    ssock = ssl_context.wrap_socket(s, server_hostname=hostname)
    ssock.connect((hostname, 443))

    # Send an HTTP GET request
    request = "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n"
    print("Sending request")
    ssock.send(request.encode('utf-8'))

    # Receive the response
    try:
        num = ssock.recv_into(buffer, 255)  # Buffer size of 4096 bytes
        
        print()
        print("Bytes received: ", num)
        print(f"First {min(num, 100)} bytes of response:")
        for i in range(min(num, 100)):
            print(chr(buffer[i]), end="")

        print()

    except TimeoutError:
        print("<<Timeout on receive>>")

    # Close the secure socket
    ssock.close()
    print("Done")


def main():
    # Configure DIOS assigned to WIZnet chip
    cs_dio = digitalio.DigitalInOut(W5500_CS_PIN)
    reset_dio = None
    
    # Initialize ethernet interface
    try:
        spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
        eth = WIZNET5K(spi_bus, cs=cs_dio, reset=reset_dio, is_dhcp=False, debug=True)

    except Exception as ex:
        print(repr(ex))
        sys.exit(-1)

    # Manually trigger DHCP discovery
    eth.set_dhcp()
    print_ifconfig(eth.ifconfig)

    # Connect using selected method
    if method_to_use == METHOD_REQUESTS:
        # FAILS using ssl with an https website
        connect_using_https_with_requests(eth)        

    elif method_to_use == METHOD_SOCKETS:
        connect_using_https_with_sockets(eth)        

    else:
        raise ValueError("Invalid connection method selected.")
        sys.exit(-1)
        
    sys.exit(0)


if __name__ == "__main__":
    print("\n<Running>")
    main()

Behavior

code.py output:

  • Initializing DHCP
    Initialising DHCP client instance.
    Requesting DHCP lease.
    DHCP FSM called with blocking = True
    FSM initial state is 1
    FSM state is INIT.
    Resetting DHCP state machine.
    Incrementing transaction ID
    FSM state is SELECTING.
    Processing SELECTING or REQUESTING state.
    Setting up connection for DHCP.
    *** Get socket.
    Allocated socket # 0
    W5K socket connect, protocol=2, port=67, ip=255.255.255.255
    *** Opening socket 0
  • Opening W5k Socket, protocol=2
    Generating DHCP message type 1
    0000 01 01 06 00 44 d3 e8 e0 00 00 00 00 00 00 00 00 ....D...........
    0010 00 00 00 00 00 00 00 00 00 00 00 00 de ad be ef ................
    0020 fe ed 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00e0 00 00 00 00 00 00 00 00 00 00 00 00 63 82 53 63 ............c.Sc
    00f0 35 01 01 0c 12 57 49 5a 6e 65 74 44 45 41 44 42 5....WIZnetDEADB
    0100 45 45 46 46 45 45 44 3d 07 01 de ad be ef fe ed EEFFEED=........
    0110 37 03 01 03 06 33 04 00 76 a7 00 ff 7....3..v...
    Calculating next retry time and incrementing retries.
    Receiving a DHCP response.
    socket_available called on socket 0, protocol 2
    Bytes avail. on sock: 308
  • Processing 8 bytes of data
    Bytes avail. on sock: 300
  • Processing 300 bytes of data
    Received 300 bytes
    Parsing DHCP message.
    Msg Type: 2
    Subnet Mask: b'\xff\xff\xff\x00'
    DHCP Server IP: b'\xc0\xa8d\x01'
    DNS Server IP: b'\xc0\xa8d\x01'
    Gateway IP: b'\xc0\xa8d\x01'
    Local IP: b'\xc0\xa8d\x8d'
    T1: 43200
    T2: 75600
    Lease Time: 86400
    Received message type 2
    *** Closing socket 0
    Waiting for socket to close…
    Socket has closed.
    FSM state is SELECTING with valid OFFER.
    FSM state is REQUESTING.
    Processing SELECTING or REQUESTING state.
    Setting up connection for DHCP.
    *** Get socket.
    Allocated socket # 0
    W5K socket connect, protocol=2, port=67, ip=255.255.255.255
    *** Opening socket 0
  • Opening W5k Socket, protocol=2
    Generating DHCP message type 3
    0000 01 01 06 00 44 d3 e8 e0 00 00 00 00 00 00 00 00 ....D...........
    0010 00 00 00 00 00 00 00 00 00 00 00 00 de ad be ef ................
    0020 fe ed 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    00e0 00 00 00 00 00 00 00 00 00 00 00 00 63 82 53 63 ............c.Sc
    00f0 35 01 03 0c 12 57 49 5a 6e 65 74 44 45 41 44 42 5....WIZnetDEADB
    0100 45 45 46 46 45 45 44 3d 07 01 de ad be ef fe ed EEFFEED=........
    0110 37 03 01 03 06 33 04 00 76 a7 00 32 04 c0 a8 64 7....3..v..2...d
    0120 8d 36 04 c0 a8 64 01 ff .6...d..
    Calculating next retry time and incrementing retries.
    Receiving a DHCP response.
    socket_available called on socket 0, protocol 2
    Bytes avail. on sock: 308
  • Processing 8 bytes of data
    Bytes avail. on sock: 300
  • Processing 300 bytes of data
    Received 300 bytes
    Parsing DHCP message.
    Msg Type: 5
    Subnet Mask: b'\xff\xff\xff\x00'
    DHCP Server IP: b'\xc0\xa8d\x01'
    DNS Server IP: b'\xc0\xa8d\x01'
    Gateway IP: b'\xc0\xa8d\x01'
    Local IP: b'\xc0\xa8d\x8d'
    T1: 43200
    T2: 75600
    Lease Time: 86400
    Received message type 5
    *** Closing socket 0
    Waiting for socket to close…
    Socket has closed.
    FSM state is REQUESTING.
    Message is ACK, setting FSM state to BOUND.
    Incrementing transaction ID
    No timers have expired. Exiting FSM.
    Found DHCP Server:
    IP: b'\xc0\xa8d\x8d'
    Subnet Mask: b'\xff\xff\xff\x00'
    GW Addr: b'\xc0\xa8d\x01'
    DNS Server: b'\xc0\xa8d\x01'
    ifconfig: 192.168.100.141 255.255.255.0 192.168.100.1 192.168.100.1

<>
_global_key_by_socketpool {<SocketPool object at 0x3c14e4c0>: 1007957744}
_global_socketpools {1007957744: <SocketPool object at 0x3c14e4c0>}
_global_ssl_contexts {1007957744: }
Get host by name
*** Get socket.
Allocated socket # 0
W5K socket connect, protocol=2, port=53, ip=192.168.100.1
*** Opening socket 0

  • Opening W5k Socket, protocol=2
  • DNS: Sending request packet...
    socket_available called on socket 0, protocol 2
    Bytes avail. on sock: 136
  • Processing 8 bytes of data
    Bytes avail. on sock: 128
  • Processing 128 bytes of data
    DNS Packet Received: b'8\x92\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\x03www\x06google\x03com\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x1e\x00\x04\x8e\xfb\xb3\x93\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x1e\x00\x04\x8e\xfb\xb3c\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x1e\x00\x04\x8e\xfb\xb3i\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x1e\x00\x04\x8e\xfb\xb3g\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x1e\x00\x04\x8e\xfb\xb3h\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x1e\x00\x04\x8e\xfb\xb3j'
    Parsing packet with ID 0x3892
  • DNS Answer Count: 6.
    Type A, class IN found in answer 1 of 6.
    IPv4 address found : 0x8efbb393.
    *** Closing socket 0
    Waiting for socket to close…
    Socket has closed.
  • Resolved IP: b'\x8e\xfb\xb3\x93'
    *** Get socket.
    Reserved sockets: [False, False, False, False, False, False, False]
    Allocated socket # 1.
    Get host by name
    *** Get socket.
    Allocated socket # 0
    W5K socket connect, protocol=2, port=53, ip=192.168.100.1
    *** Opening socket 0
  • Opening W5k Socket, protocol=2
    Error : RuntimeError('pystack exhausted',)

Code done running.

Description

Bug Report:
Hard crash due to RuntimeError("pystack exhausted") when using 'adafruit_requests' to connect to an https website (google) on 2 different hardware platforms using Wiznet5K Ethernet phy chips.

Hardware:

  1. Adafruit ESP32-S3 feather (4MB FLASH / 2MB PSRAM) + Adafruit Ethernet Featherwing
  2. RP2040 PicoW + Adafruit Ethernet Featherwing (with minor modifications to the example code to address the differences between this platform and the ESP32-S3 platform)

Circuitpython:
Version 9.2.7

Description:
I've been attempting to evaluate the use of hardwired Ethernet via Wiznet5K chips and Circuitpython. When using the adafruit_requests package and attempting to connect to an https site (google), I get a hard crash due to a RuntimeError("pystack exhausted") on 2 different hardware platforms. If I don't use adafruit_requests and instead just use the sockets module along with the ssl module, I am able to successfully connect and receive the response.

I've attached my simple POC code that reproduces this behavior. As listed, the code runs the "https with adafruit_requests" test and demonstrates the failure mode. To see the successful use of socket + ssl to connect, change the line 'method_to_use = METHOD_REQUESTS' to 'method_to_use = METHOD_SOCKETS'.

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0