memory leak in secure connection

ememberus
Posts: 21
Joined: Thu May 04, 2017 12:53 am

memory leak in secure connection

Postby ememberus » Thu May 04, 2017 1:14 am

There is an apparent memory leak in SDK v2.0.0 when
espconn_secure_connect() is used instead of espconn_connect().

The issue appears only when RF signal is weak or disrupted
due to RF interference, obstacles between ESP8266 and AP etc,
and client connection is temporarily lost.

I attach 2 screenshots of ThingSpeak plots of "free heap" memory traces:

1. heap memory after RF signal is disrupted several times when using a secure connection
free_heap_port_443.PNG
free_heap_port_443.PNG (8.45 KiB) Viewed 1132 times


2. heap memory after RF signal is similarly disrupted when using unsecure connection
free_heap_port_80.PNG
free_heap_port_80.PNG (8.95 KiB) Viewed 1132 times

Her Majesty
Posts: 155
Joined: Mon Oct 27, 2014 11:09 am

Re: memory leak in secure connection

Postby Her Majesty » Thu May 04, 2017 5:08 pm

Here is a SSL demo, seems do not have this problem? http://bbs.espressif.com/viewtopic.php?f=31&t=389&p=8330#p8330

ememberus
Posts: 21
Joined: Thu May 04, 2017 12:53 am

Re: memory leak in secure connection

Postby ememberus » Wed May 10, 2017 9:10 am

It is hard to say whether leak is happening in your test or not -
I had to modify your code to simulate my conditions, but so far it did not produce anything conclusive.
Results seem very strange, though. First connection opens quickly, but all others that follow are very
unstable, crashing CPU from time to time. I cannot run my test long enough to say there is no leak
or it is there.

Please, take a look at my test code and tell whether you see anything wrong.

FYI:
- my test runs HTTP client periodically, opening a connection and closing it after each send/receive exchange
- in order to weaken RF signal and cause periodic disconnects, I put esp8266 into antistatic bag
- random RF disconnects are crucial, if RF signal is strong then memory leak (and lately crashes) never occur

Code: Select all

// SDK v2.0.0, non-RTOS
#include "mem.h"

#include <c_types.h> // uint8 etc
#include <ip_addr.h> // ip_addr_t
#include <espconn.h> // espconn_gethostbyname() etc
#include <os_type.h> // os_timer_t
#include <osapi.h> // os_printf()


#include <user_interface.h>

#include "user_config.h"


#define DNS_ENABLE

unsigned char *default_certificate;
unsigned int default_certificate_len = 0;
unsigned char *default_private_key;
unsigned int default_private_key_len = 0;

//#define NET_DOMAIN "iot.espressif.cn"
#define NET_DOMAIN "api.thingspeak.com"

#ifndef SNTP_START
   #define SNTP_START()
#endif

#ifndef DEBUG_REPORT
   #define DEBUG_REPORT(...)
#endif

void *pvPortMalloc(size_t xWantedSize, const char *file, int line);
void *pvPortZalloc(size_t, const char *file, int line);
void *pvPortRealloc(void *, size_t xWantedSize, const char *file, int line);
void vPortFree(void *ptr, const char *file, int line);
void *vPortMalloc(size_t xWantedSize, const char *file, int line);
void pvPortFree(void *ptr, const char *file, int line);
int os_printf_plus(const char *format, ...)  __attribute__ ((format (printf, 1, 2)));
int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
size_t ets_strlen(const char *s);
int ets_sprintf(char *str, const char *format, ...)  __attribute__ ((format (printf, 2, 3)));
void *ets_memcpy(void *dest, const void *src, size_t n);
void ets_timer_arm_new(ETSTimer *a, int b, int c, int isMstimer);
void ets_timer_disarm(ETSTimer *a);
void ets_timer_setfn(ETSTimer *t, ETSTimerFunc *fn, void *parg);

LOCAL void user_check_ip (void * args);


char http_buf[20000];
bool tcp_connected = false;

#define packet_size   (2 * 1024)

LOCAL os_timer_t test_timer;
LOCAL os_timer_t send_timer;
LOCAL struct _esp_tcp user_tcp;
LOCAL struct espconn user_tcp_conn = { ESPCONN_TCP, ESPCONN_NONE, { &user_tcp } };
LOCAL ip_addr_t tcp_server_ip = { 0 };

/******************************************************************************
 * FunctionName : user_tcp_connect_cb
 * Description  : A new incoming tcp connection has been connected.
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_tcp_connect_cb (void *arg)
{
//   struct espconn *pespconn = arg;

   os_timer_disarm(&test_timer);

   os_printf("user_tcp_connect_cb: connected !!!\r\n");
   tcp_connected = true;
   DEBUG_REPORT(CONNECT);
}

/******************************************************************************
 * FunctionName : user_tcp_recon_cb
 * Description  : reconnect callback, error occured in TCP connection.
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_tcp_recon_cb (void *arg, sint8 err)
{
   // if connection err, then user can try to reconnect here.
   struct espconn * pespconn = (struct espconn *) arg;

   DEBUG_REPORT(RECONNECT);
   switch (err)
   {
      case ESPCONN_OK:
         os_printf ("user_tcp_recon_cb: TCP connected\r\n");
         tcp_connected = true;
         break;
      default:
         os_printf("user_tcp_recon_cb: TCP connect error %d, reconnecting... !!!\r\n",err);
         tcp_connected = false;
            espconn_secure_connect(pespconn); // tcp SSL connect
   }
}

/******************************************************************************
 * FunctionName : user_tcp_discon_cb
 * Description  : disconnect callback.
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_tcp_discon_cb (void *arg)
{
//   struct espconn *pespconn = arg;

   tcp_connected = false;
   os_printf("user_tcp_discon_cb: TCP disconnected !!!\r\n");
//   espconn_secure_disconnect (arg);
//   espconn_delete (arg);
   DEBUG_REPORT(DISCONNECT);
}

/******************************************************************************
 * FunctionName : user_tcp_sent_cb
 * Description  : data sent callback.
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_tcp_sent_cb (void *arg)
{
    os_printf("user_tcp_sent_cb: TCP data sent !!!\r\n");
}

/******************************************************************************
 * FunctionName : user_tcp_recv_cb
 * Description  : receive callback.
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_tcp_recv_cb (void *arg, char *pusrdata, unsigned short length)
{
//    os_printf("user_tcp_recv_cb: data received !!!\r\n%s\r\n", pusrdata);
    os_printf("user_tcp_recv_cb: TCP %d bytes received !!!\r\n", length);
}

/******************************************************************************
 * FunctionName : user_esp_platform_sent
 * Description  : Processing the application data and sending it to the host
 * Parameters   : pespconn -- the espconn used to connection with the host
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_send (void * args)
{
   static int count = 0;
   struct espconn * pespconn = (struct espconn *) args;

   os_timer_disarm(&test_timer);
   count++;

   if (tcp_connected)
   {
      unsigned free_heap_size = system_get_free_heap_size ();
      os_printf ("free heap = %d\r\n", free_heap_size);
      os_sprintf(http_buf,
            "GET /update?api_key=%s&field1=%d&field2=%d HTTP/1.1\r\n"
            "Host: %s:%d\r\n"
            "Connection: close\r\n"
            "User-Agent: ESP8266\r\n"
            "\r\n",
            API_KEY, count, free_heap_size, "api.thingspeak.com", 443);

      espconn_secure_sent (pespconn, (uint8 *) http_buf, os_strlen(http_buf));
   }
   else
   {
      os_printf ("user_sent_data: TCP not connected\r\n");
      os_timer_setfn(&test_timer, user_check_ip, NULL);
      os_timer_arm(&test_timer, 100, 0);
   }
}

#ifdef DNS_ENABLE
/******************************************************************************
 * FunctionName : user_dns_found
 * Description  : dns found callback
 * Parameters   : name -- pointer to the name that was looked up.
 *                ipaddr -- pointer to an ip_addr_t containing the IP address of
 *                the hostname, or NULL if the name could not be found (or on any
 *                other error).
 *                callback_arg -- a user-specified callback argument passed to
 *                dns_gethostbyname
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_dns_found_cb(const char *name, ip_addr_t *ipaddr, void *arg)
{
    struct espconn *pespconn = (struct espconn *)arg;

    if (ipaddr == NULL)
    {
        os_printf("user_dns_found: invalid pointer NULL\r\n");
        return;
    }

    os_timer_disarm(&test_timer);

    os_printf("user_dns_found: %d.%d.%d.%d\r\n",
            *((uint8 *)&ipaddr->addr), *((uint8 *)&ipaddr->addr + 1),
            *((uint8 *)&ipaddr->addr + 2), *((uint8 *)&ipaddr->addr + 3));

   if (tcp_server_ip.addr == 0) // first time initializing connection?
   {
      if (ipaddr->addr != 0)
      {
         // dns succeed, create tcp connection

         if (!tcp_connected)
         {
            tcp_server_ip.addr = ipaddr->addr;
            os_memcpy(pespconn->proto.tcp->remote_ip, &ipaddr->addr, 4); // remote ip of tcp server which get by dns

            pespconn->proto.tcp->remote_port = 443; // remote SSL port of tcp server

            pespconn->proto.tcp->local_port = espconn_port(); //local port of ESP8266

            espconn_regist_connectcb(pespconn, user_tcp_connect_cb); // register connect callback
            espconn_regist_reconcb(pespconn, user_tcp_recon_cb); // register reconnect callback as error handler
            espconn_regist_disconcb(pespconn, user_tcp_discon_cb);  // register disconnect callback here, because that SSL handshake may fail.
            espconn_regist_recvcb(pespconn, user_tcp_recv_cb);
            espconn_regist_sentcb(pespconn, user_tcp_sent_cb);

            espconn_secure_set_size(ESPCONN_CLIENT,5120); // set SSL buffer size, if your SSL packet larger than 2048 bytes
            espconn_secure_connect(pespconn); // tcp SSL connect
         }
         else
            os_printf("user_dns_found: TCP already connected\r\n");
      }
      else
         os_printf("user_dns_found: unknown dns error\r\n");
   }
   else // remote IP resolved
   {
         if (!tcp_connected)
         {
            espconn_secure_set_size(ESPCONN_CLIENT,5120); // set SSL buffer size, if your SSL packet larger than 2048 bytes
            espconn_secure_connect(pespconn); // tcp SSL connect
         }

         os_timer_arm(&test_timer, 1000, 0); // resume dns check
    }
}

/******************************************************************************
 * FunctionName : user_esp_platform_dns_check_cb
 * Description  : 1s time callback to check dns found
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
user_dns_check_cb (void *arg)
{
    struct espconn *pespconn = arg;

    os_timer_disarm(&test_timer);

    if (tcp_server_ip.addr == 0) // remote IP not resolved?
   {
      ip_addr_t remote_ip = { 0 };

      sint8 res = espconn_gethostbyname (pespconn, NET_DOMAIN, &remote_ip, user_dns_found_cb);

      switch (res)
      {
         case ESPCONN_OK:
            os_printf ("DNS request succeed\r\n");
            break;
         case ESPCONN_INPROGRESS:
            os_printf ("DNS request in progress...\r\n");
            break; // OK
         default:
            os_printf ("DNS request failed %d\r\n", res);
      }

      os_timer_arm(&test_timer, 1000, 0); // resume IP check
   }
   else // remote IP resolved
   if (!tcp_connected)
   {
      os_printf ("user_dns_check_cb: IP resolved, connecting...\r\n");
      espconn_secure_connect(pespconn); // tcp SSL connect

      os_timer_arm(&test_timer, 10000, 0); // resume IP check
   }
}
#endif

/******************************************************************************
 * FunctionName : user_check_ip
 * Description  : check whether get ip addr or not
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
user_check_ip (void * dummy)
{
    struct ip_info ipconfig;

   //get ip info of ESP8266 station
    wifi_get_ip_info(STATION_IF, &ipconfig);

    if (wifi_station_get_connect_status() == STATION_GOT_IP && ipconfig.ip.addr != 0)
    {
        os_printf("user_check_ip: WiFi connected !!!\r\n");

//      os_timer_disarm (&test_timer);
//      os_timer_setfn (&test_timer, user_dns_check_cb, &user_tcp_conn);
        user_dns_check_cb (&user_tcp_conn);
    }
    else
    {
        if ((wifi_station_get_connect_status() == STATION_WRONG_PASSWORD ||
            wifi_station_get_connect_status() == STATION_NO_AP_FOUND ||
            wifi_station_get_connect_status() == STATION_CONNECT_FAIL))
        {
            os_printf("user_check_ip: WiFi connect fail !!!\r\n");
        }
        else
        {
            os_printf("user_check_ip: WiFi not connected !!!\r\n");
            // re-arm timer to check ip
            os_timer_disarm(&test_timer);
            os_timer_setfn(&test_timer, user_check_ip, NULL);
            os_timer_arm(&test_timer, 100, 0);
        }
    }
}


/******************************************************************************
 * FunctionName : user_set_station_config
 * Description  : set the router info which ESP8266 station will connect to
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
user_set_station_config ()
{
   // Wifi configuration
   char ssid[32] = SSID;
   char password[64] = PASSWORD;
   struct station_config stationConf;

   // need not mac address
   stationConf.bssid_set = 0;
   
   // set ap settings
   os_memcpy(&stationConf.ssid, ssid, 32);
   os_memcpy(&stationConf.password, password, 64);
   wifi_station_set_config(&stationConf);

   // set a timer to check whether got ip from router succeed or not.
   os_timer_disarm(&test_timer);
   os_timer_setfn(&test_timer, user_check_ip, NULL);
   os_timer_arm(&test_timer, 100, 0);

}


/******************************************************************************
 * FunctionName : user_init
 * Description  : entry of user application, init user function here
 * Parameters   : none
 * Returns      : none
*******************************************************************************/

void user_init ()
{
   io_init (); // UART0 etc
   os_printf("SDK version:%s\n", system_get_sdk_version());
   
   // set station mode
   wifi_set_opmode(STATION_MODE);

   // ESP8266 connect to AP
   user_set_station_config();

   SNTP_START();

   os_timer_disarm(&send_timer);
   os_timer_setfn(&send_timer, user_send, &user_tcp_conn);
   os_timer_arm(&send_timer, 20000, true);
}

ememberus
Posts: 21
Joined: Thu May 04, 2017 12:53 am

Re: memory leak in secure connection

Postby ememberus » Fri May 12, 2017 1:14 am

This startup log also shows TCP reconnect failures.
While dns_check_cb reads WiFi status as "connected",
TCP client is repeatedly unable to connect and free
heap memory slowly goes down.

Once rebooted, the board immediately connects
to AP and to remote server. Sometimes this releases
allocated heap memory, but sometimes not.

Code: Select all

user_check_ip: WiFi not connected !!!
user_check_ip: WiFi not connected !!!
user_check_ip: WiFi not connected !!!
user_check_ip: WiFi not connected !!!
user_check_ip: WiFi not connected !!!
user_check_ip: WiFi not connected !!!
ip:10.0.0.43,mask:255.255.255.0,gw:10.0.0.1
user_check_ip: WiFi connected !!!
dns_check_cb: DNS request in progress...
dns_resolved_cb: 52.1.229.129
please start sntp first !
client handshake start.
client handshake ok!
please start sntp first !
tcp_connect_cb: connected !!!
pm open,type:2 0
user_send: free heap = 16904
tcp_sent_cb: TCP data sent !!!
tcp_recv_cb: TCP 638 bytes received !!!
client's data invalid protocol
Error: SSL error 3
2017-05-11 16:43:42 GMT
tcp_disconnect_cb: TCP disconnected !!!
user_sent_data: TCP not connected
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
2017-05-11 16:44:08 GMT
tcp_reconnect_cb: TCP connect error -11, reconnecting... !!!
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
client handshake start.
client handshake ok!
2017-05-11 16:44:13 GMT
tcp_connect_cb: connected !!!
2017-05-11 16:44:14 GMT
tcp_reconnect_cb: TCP connect error -11, reconnecting... !!!
user_sent_data: TCP not connected
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
user_sent_data: TCP not connected
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
user_sent_data: TCP not connected
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
client's data invalid protocol
Error: SSL error 3
2017-05-11 16:45:13 GMT
tcp_disconnect_cb: TCP disconnected !!!
user_sent_data: TCP not connected
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
client handshake start.
client handshake ok!
2017-05-11 16:45:22 GMT
tcp_connect_cb: connected !!!
user_send: free heap = 16704
tcp_sent_cb: TCP data sent !!!
tcp_recv_cb: TCP 638 bytes received !!!
client's data invalid protocol
Error: SSL error 3
2017-05-11 16:45:42 GMT
tcp_disconnect_cb: TCP disconnected !!!
user_sent_data: TCP not connected
user_check_ip: WiFi connected !!!
dns_check_cb: remote IP resolved, connecting...
client handshake start.
client handshake ok!
2017-05-11 16:46:02 GMT
tcp_connect_cb: connected !!!
user_send: free heap = 16704
tcp_sent_cb: TCP data sent !!!
tcp_recv_cb: TCP 638 bytes received !!!


Please, note another issue with connect/disconnect events.
Last TCP disconnect comes out of nowhere -
there is no prior matching "connect" event ( :o )

Overall, the pattern may vary slightly in details, but mostly
keep repeating itself on my board.

ememberus
Posts: 21
Joined: Thu May 04, 2017 12:53 am

Re: memory leak in secure connection

Postby ememberus » Fri May 12, 2017 1:19 am

Correction to my previous post:
a successful connect may sometimes release allocated heap memory.
A CPU reboot will obviously clear all prior heap allocations.

User avatar
pratik
Posts: 450
Joined: Wed Jun 29, 2016 7:17 pm
Location: India
Contact:

Re: memory leak in secure connection

Postby pratik » Sun May 28, 2017 10:26 pm

Are you using the libssl.a or the libmbedtls.a?
Regards,
Pratik Panda
Website: http://www.PratikPanda.com

Custom firmware, Knowledge base and freelancing (ESP8266/ESP32):
http://www.iot-bits.com

ememberus
Posts: 21
Joined: Thu May 04, 2017 12:53 am

Re: memory leak in secure connection

Postby ememberus » Wed May 31, 2017 2:54 am

Hi
I link with libssl.a and run either on WeMos or ESP12,
i.e. not using mbed.

Dashaduamp
Posts: 5
Joined: Mon Jun 19, 2017 10:28 am

memory leak in secure connection

Postby Dashaduamp » Wed Jun 21, 2017 10:38 am

19MB isnt a memory leak ;p

A leak is memory usage that continues to grow and never stops.

19MB is operating within its proper bounderies.

v4.9 is no longer available...

ememberus
Posts: 21
Joined: Thu May 04, 2017 12:53 am

Re: memory leak in secure connection

Postby ememberus » Wed Jun 21, 2017 10:06 pm

Dashaduamp, I did not say that it ever stopped.
I also not sure how 19Mb or v4.9 matters.
Care to clarify?

You may also want to look into this recent thread:
viewtopic.php?f=66&t=4701

Alltogether, these findings point to possible memory leak(s) in SDK TCP stack.

Who is online

Users browsing this forum: No registered users and 4 guests