Closing a socket after recv timeout results in resource leak

chrismerck
Posts: 76
Joined: Tue May 29, 2018 8:00 pm

Closing a socket after recv timeout results in resource leak

Postby chrismerck » Thu Oct 25, 2018 11:12 pm

tl;dr: I get an error after closing 10x sockets, but only if they just experienced a recv() timeout.

Note: I'm using SO_RECVTIMEO option set to 1 second. So a recv() call will return after 1 sec if no data is received. However, the socket seems to then be in a state where it is not closed properly when close() is called.

I suspect this is an lwIP bug, and would like to try upgrading to a recent lwIP release, but there seem to be many ESP-specific changes to lwIP? Should I try https://github.com/espressif/esp-lwip ?

---

To reproduce the bug:

1) use simple_wifi.c example in softAP mode
2) add the function pasted at the bottom of this post
3) connect to esp32 wifi
4) on PC, do `nc 192.168.4.1 8800`
5) let it time out without typing anything (1sec)
6) repeat 11x
7) note that on the 11th time, the accept fails with "Too many open files in system".

Here's the log output on a normal timeout:

Code: Select all

tcp_slowtmr: processing active pcb
tcp_slowtmr: polling application
lwip_recvfrom: netconn_recv err=-3, netbuf=0x0
lwip_recvfrom(63): buf == NULL, error is "Timeout."!
read err while processing headers (possibly a timeout): -1
lwip_close: (63)
lwip_close: is_tcp=1
tcp_close: closing in State: ESTABLISHED
TCP connection closed: FIN_WAIT_2 59689 -> 8800.
tcp_pcb_purge
close 0
lwip_accept(54)...
tcp_slowtmr: no active pcbs
Here's the log output after the 11th timeout:

Code: Select all

tcp_slowtmr: no active pcbs
TCP connection request 59690 -> 8800.
TCP connection established 59690 -> 8800.
tcp_close: closing in State: ESTABLISHED
TCP connection closed: FIN_WAIT_2 59690 -> 8800.
tcp_pcb_purge
accepted client_sock -1
failed to accept: -1 Too many open files in system
lwip_accept(54)...
tcp_slowtmr: no active pcbs
tcp_slowtmr: no active pcbs

Code: Select all


#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

void ill_fated_server()
{
  int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sock < 0) {
    printf("failed to create socket: %d %s\n", sock, strerror(errno));
    return;
  }

  #if 0
  /* attempt to re-use previous port */
  int on = 1;
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
    printf("setsockopt SO_REUSEADDR failed: %s\n", strerror(errno));
  }
  #endif

  struct sockaddr_in serverAddress;

  /* bind to the http port */
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddress.sin_port = htons(8800);
  int ret;
	ret = bind(sock, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
	if (ret < 0) {
		printf("failed to bind: %d %s\n", ret, strerror(errno));
    return;
	}

  /* start listening for connections */
  ret = listen(sock, 5);
  if (ret < 0) {
		printf("failed to listen: %d %s\n", ret, strerror(errno));
    return;
  }

  while (1) {

    /* accept */
    struct sockaddr_in clientAddress;
    socklen_t clientAddressLength = sizeof(clientAddress);
    int client_sock = accept(sock,
      (struct sockaddr *) &clientAddress,
      &clientAddressLength);
    printf("accepted client_sock %d\n", client_sock);
    if (client_sock < 0) {
      printf("failed to accept: %d %s\n", client_sock, strerror(errno));
      continue;
    }

    /* set receive timeout */
    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    if (setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO,
                  &tv,
                  sizeof(tv)) < 0) {
      printf("failed to set httpd socket receive timeout: %s\n",
        strerror(errno));
    }

    /* get one byte */
    char c;
    ret = recv(client_sock, (uint8_t *) &c, 1, 0);
    if (ret == 0) {
      printf("client hung up unexpectedly\n");
    } else if (ret < 0) {
      printf("read err while processing headers (possibly a timeout): %d\n", ret);
    } else {
      printf("recv char 0x%02x '%c'\n", c, c);
    }

    /* close */
    ret = close(client_sock);
    printf("close %d\n", ret);

  }

}

chrismerck
Posts: 76
Joined: Tue May 29, 2018 8:00 pm

Re: Closing a socket after recv timeout results in resource leak

Postby chrismerck » Sat Apr 06, 2024 2:00 pm

FWIW we still face this issue in our fw on IDF v5.1.1

Who is online

Users browsing this forum: Google [Bot] and 197 guests