libssh2 leak on write to sftp server

Gianluca Bortolozzo Gianluca at debugsrl.it
Wed Oct 16 10:30:39 CEST 2024


Dear all,

I'm testing a sw. with the device Espressif esp32/WROVER-E,  with esp-idf  4.4.3 and  with visualgdb  (a cross platform based on visual studio).
The server is a notebook with windows10 and OpenSSF free version by the link
https://github.com/PowerShell/Win32-OpenSSH

During test with wifi/ethernet test to read/write file into a local server sftp I discovered that: the heap is stable if I read file from server,
If I write a file to a server sftp start to decrease of 80byte every operation the heap available.

I double check a lot alloc and free, it seem ok but the sfpt_open or write reduce the heap.
The files are text file of json developed on write (250 bytes of size) ,  decoded when I read from server sftp.
The version of libssh2 is
LIBSSH2_VERSION                             "1.11.1_DEV"

Attached the file of test where the functions used are:

void Manager_client_sftp(void) manage the operation read write into server sftp

and

int InvioJsonIdPowerOnIdentification (LIBSSH2_SESSION *session)

that write the file to the server sftp and decrease the heap

Did you have ever seen this this problem ?
If yes are ther a solution?

In the same time I'm starting the porting to last esp-idf into visualcode ide with plugin espressif
Because with visualgdb does not support the last revison of esp-idf.

Many thanks

Best Regards

     Gianluca Bortolozzo
                 Debug s.r.l.
                 Via della Meccanica 1T
                 36100 VICENZA
                 Tel. +39 0444-566344
                  Fax +39 0444-962273
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.haxx.se/pipermail/libssh2-devel/attachments/20241016/226a0a1b/attachment-0001.htm>
-------------- next part --------------
// invia il json del ID di POWER ON
// parameters: session, la sessione sftp aperta
// return:  2, comando non richiesto
//          0, tutto ok scritto
//          -1, errore server sftp 
int InvioJsonIdPowerOnIdentification (LIBSSH2_SESSION *session)
{
    cJSON *root;
    int result = 2;
    ssize_t nwritten;
    //LIBSSH2_SFTP *sftp_session = NULL;
    // LIBSSH2_SFTP_HANDLE *sftp_handle = NULL;
    uint8_t aux_len;

    // e' un power on si deve inviare il file json
    printf("JSON: ID_POWER_ON/IDENTIFICATION\r\n");

    // TODO: gestire la problematica di server sFTP che non si connette o altro

    root = cJSON_CreateObject ( );
    cJSON_AddStringToObject(root, STR_ID_JSON, JSON_ID_TRANSMIT_IDENTIFICATION);
    cJSON_AddStringToObject(root, STR_SERIAL_NUMBER, app_Machine.Codici.Matricola);
    cJSON_AddStringToObject(root, STR_TRAIN_NUMBER, app_Machine.Codici.NumeroTreno);
    cJSON_AddStringToObject(root, STR_TRAVEL_NUMBER, app_Machine.Codici.NumeroViaggio);

   	  
    SetTimeZoneItaly ( );

    // preparazione del prefisso e poi del nome del file
    GenerazionePrefissoFileJson(nomeFileJson, LEN_MAX_NAME_FILE);
    // si salva il prefisso poiche' servira' a controllare le risposte ricevute e trovare l'ultima
    memset(str_data_req_travel_code, 0, LEN_STR_DATA);
    strcpy(str_data_req_travel_code, nomeFileJson);
    aux_len = strlen(nomeFileJson);
    sprintf(nomeFileJson + aux_len, "%s_", app_Machine.Codici.NumeroTreno);
    aux_len = strlen(nomeFileJson);
    sprintf(nomeFileJson + aux_len, "%s_", app_Machine.Codici.Matricola);
    aux_len = strlen(nomeFileJson);
    sprintf(nomeFileJson + aux_len, "%s", STR_IDENTIFICATION_FILE);

    // scrittura del file sul server
    // generazione di nome file in base a data e ora , con json
    sftp_session = libssh2_sftp_init (session);

    if (!sftp_session)
    {
	    fprintf(stderr, "Unable to init SFTP session\n");

	    // stato di errore
	    result = -1;
    } 
    else
    {
    int len;
    esp_task_wdt_reset();

    // Request to write a file via SFTP
    // do
   // {
	    sftp_handle = libssh2_sftp_open(sftp_session,
		    nomeFileJson,
		    LIBSSH2_FXF_WRITE |
			    LIBSSH2_FXF_CREAT |
			    LIBSSH2_FXF_TRUNC, 
		    LIBSSH2_SFTP_S_IRUSR |  
			    LIBSSH2_SFTP_S_IWUSR | 
			    LIBSSH2_SFTP_S_IRGRP | 
	 	    LIBSSH2_SFTP_S_IROTH); 

   // } while (libssh2_sftp_last_error(sftp_session) == LIBSSH2_ERROR_EAGAIN);
		 
    if (!sftp_handle)
    {
        fprintf (stderr,
        "Unable to open file with SFTP: %ld\n",
        libssh2_sftp_last_error (sftp_session));

        // stato di errore
        result = -1;
    } 
    else
    {        
	    my_json_string = cJSON_Print(root);
	    begin = my_json_string;
	    
	    /// ESP_LOGI(TAG, "json_string\n%s", my_json_string);
	    len = strlen(my_json_string);
        do
        {
            // write data in a loop until we block
	        nwritten = libssh2_sftp_write(sftp_handle, my_json_string, len);
	        my_json_string += nwritten;
            len -= nwritten;
	        
	        // HACK: serve per non bloccare il modbus altrimenti con il timeout rimanda comandi di reinizializzazione
	        vTaskDelay(10);
	        
        } while (len > 0);
	        
	    if (begin) 
	    {
		    cJSON_free(begin);
	    }
	    
	    if (root)
	    {	            
		    cJSON_Delete(root);
	    }
    }
		  
    if (sftp_handle)
        libssh2_sftp_close (sftp_handle);
    }
     
    // if (sftp_session)
    {
    libssh2_sftp_shutdown (sftp_session);
    }
    
    return result;
}
//------------------------------------------------------------------------------


void Manager_client_sftp(void)
{
  static uint8_t old_state=255;	
  static struct sockaddr_in sin;

  vTaskDelay(20);
  if (state != old_state)
  {
	  printf("Manager_client_sftp state = %d\n",state);
	  old_state = state;
  }

  // gestione anomalie che impediscono l'esecuzione del client sftp
  if (app_Machine.SftpServerSetup.SoloServerSftp == 0)
  {
    // server sftp non abilitato si mette la macchina a stati in idle
    if (state == STATE_IDLE)
    {
      return;
    }
    state = STATE_SHUTDOWN;
  }

  // gestione anomalie che impediscono l'esecuzione del client sftp
  if (chunk_operation_running != false)
  {
    // server sftp non abilitato si mette la macchina a stati in idle
    if (state == STATE_IDLE)
    {
      return;
    }
    state = STATE_SHUTDOWN;
  }
  
  esp_task_wdt_reset();

  switch (state)
  {
    case 0:
      {
        // carica dalla flash interna le impostazioni se presenti
        if (doneRegistration == false)
        {
          doneRegistration = true;

          if (InitializeVariableAccountSftp ( ) != ESP_OK)
          {
              printf ("errore nella lettura variabili account sftp\n\r");
          }

          if (account_sFTP_registered == false)
          {
              if (InitializeAccountSftp2 (&account_sftp) == 0)
              {
                  account_sFTP_registered = true;
              }
          }

          if (key_private_registered == false)
          {
              if (InitializePrivateKeySftp2 ( ) == 0)
              {
                  key_private_registered = true;
              }
          }

          if (key_public_registered == false)
          {

              if (InitializePublicKeySftp2 ( ) == 0)
              {
                  key_public_registered = true;
              }
          }

          if ((account_sFTP_registered == false) || (key_private_registered == false) || (key_public_registered == false))
          {
              // non sono presenti le credenziali per cui si riprova e lo si segnala
              app_Machine.Status.DoubleWord &= ~(1 << APP_STATUS_ACCOUNT_SFTP_REGISTRATO);
              return;
          } 
          else if ((account_sFTP_registered == true) && (key_private_registered == true) && (key_public_registered == true))
          {
            // sono presenti le credenziali e ci si e' connessi
            app_Machine.Status.DoubleWord |= 1 << APP_STATUS_ACCOUNT_SFTP_REGISTRATO;
          }

          // qui sftp_session deve essere null serve nel caso si vada in shutdown senza
          // aver inizializzato correttamente sft_session
        }

	    if ((app_command_sftp.Comand.DoubleWord & (1 << APP_ID_TRANSMIT_ID)) != 0)
	    {
		    // al power on si deve fare richiesta del numero di viaggio
		    app_command_sftp.Comand.DoubleWord &= ~(1 << APP_ID_TRANSMIT_ID);
		    fl_APP_ID_TRANSMIT_ID = true;
		    sftp_session = NULL;
		    state = 10;
	    }
	    else if  ((app_command_sftp.Comand.DoubleWord & (1 << APP_ID_DELIVERY_DONE)) != 0)
	    { 
		    app_command_sftp.Comand.DoubleWord &= ~(1 << APP_ID_DELIVERY_DONE);
		    fl_NEW_DELIVERY_ID = true;
		    sftp_session = NULL;
		    state = 10;
	    }
	    else   if ((app_command_sftp.Comand.DoubleWord & (1 << APP_ID_NEW_TRAVEL_NUMBER)) != 0)
	    {
		    app_command_sftp.Comand.DoubleWord &=  ~(1 << APP_ID_NEW_TRAVEL_NUMBER);
		    fl_NEW_TRAVEL_NUMBER_ID = true;
		    sftp_session = NULL;
		    state = 10;
	    }
	    else if ((app_command_sftp.Comand.DoubleWord & (1 << APP_ID_CHECK_TRAVEL_NUMBER)) != 0)
	    {
		    app_command_sftp.Comand.DoubleWord &= ~(1 << APP_ID_CHECK_TRAVEL_NUMBER);
		    fl_CHECK_TRAVEL_NUMBER_ID = true;
		    sftp_session = NULL;
		    state = 10;
	    }
	    else if ((app_command_sftp.Comand.DoubleWord & (1 << APP_ID_CODE_DISCOUNT)) != 0)
	    {
		    app_command_sftp.Comand.DoubleWord &= ~(1 << APP_ID_CODE_DISCOUNT);
		    fl_CHECK_CODE_DISCOUNT_ID = true;
		    sftp_session = NULL;
		    state = 10;
	    }
      }
      break;
	   
    case 10:

      // controllo della memoria disponibile se poca resetta
      CheckFreeSpaceOnHeap();

      // inizia la parte ssh di test derivata da sftp_write.c di libssh2
	    rc = libssh2_init(0);

	    if (rc)
	    {
		    fprintf(stderr, "libssh2 initialization failed (%d)\n", rc);
		    // stato di errore
		    state = STATE_SHUTDOWN;
	    }
	    else
	    {
		    state = 11;
	    }
	    break;
	  
    case 11:
	    // The application code is responsible for creating the socket
	    // and establishing the connection
	    sock = socket(AF_INET, SOCK_STREAM, 0);
	    if (sock == LIBSSH2_INVALID_SOCKET) 
	    {
		    fprintf(stderr, "failed to create socket.\n");
		    // stato di errore si va in shutdown per fare il recovery
		    state = STATE_SHUTDOWN;
	    }
	    else
	    {	  
		    sin.sin_family = AF_INET;
		  
		    // si imposta a 1151 il default dovrebbe essere 22 ma si deve poter impostare
		    sin.sin_port = htons(app_Machine.SftpServerSetup.PortaServerSftp);
        memcpy(&sin.sin_addr.s_addr, &app_Machine.SftpServerSetup.IpServerSftp, 4);   
		    state = 12;
	    }
	    break;
	  
	case 12:
	  {
		  int r;
		  r = connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in));
		  if (r != 0) 
		  {
			  fprintf(stderr, "r = %d xx failed to connect.\n", r);

			  // stato di errore
			  state = STATE_SHUTDOWN;
		  }
		  else
		  {
			  state = 13;
		  }
	  }
	  break;
	  
	case 13:
	  // Create a session instance 
	  session = libssh2_session_init();

    if (!session)
    {
      fprintf (stderr, "Could not initialize SSH session.\n");
      // stato di errore
      state = STATE_SHUTDOWN;
    } 
    else
    {
      // Since we have set non-blocking, tell libssh2 we are blocking
      libssh2_session_set_blocking (session, 1);
      state = 14;
    }
    break;
	  
  case 14:
	  // ... start it up. This will trade welcome banners, exchange keys,
	  // and setup crypto, compression, and MAC layers 
	  rc = libssh2_session_handshake(session, sock);

	  if (rc) 
	  {
		  fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
		  // stato di errore
		  state = STATE_SHUTDOWN;
	  }
	  else
	  {
		  state = 15;
	  }
	  break;
	
  case 15:
	  // At this point we have not yet authenticated.  The first thing to do
    // is check the hostkey's fingerprint against our known hosts Your app
	  // may have it hard coded, may go to a file, may present it to the
	  // user, that's your call 
	  fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);

    // si confronta  il fingerprint ottenuto con quello ottenuto dalle credenziali di accesso
    // se serve
	  state = 16;
	  break;
	
	case 16:
	  // check what authentication methods are available 

    // si deve verificare che tutto sia definito altrimenti non si parte
    if ((account_sFTP_registered != true) || (key_private_registered != true) || (key_public_registered != true))
    {
      RequestReinitSftp ( );
      return;
    }

    userauthlist = libssh2_userauth_list(session, account_sftp.username, strlen(account_sftp.username));
    fprintf(stderr, "Authentication methods: %s\n", userauthlist);
	  if (strstr(userauthlist, "password") != NULL) 
	  {
		  auth_pw |= 1;
	  }
  
	  if (strstr(userauthlist, "keyboard-interactive") != NULL) 
	  {
		  auth_pw |= 2;
	  }
 
	  if (strstr(userauthlist, "publickey") != NULL) 
	  {
		  auth_pw |= 4;
	  }  
	  state = 17;
	  break;
	  
  case 17:
	  if ( (auth_pw & 2) == auth_pw) 
	  {
		  // We could authenticate via password 
		  if (libssh2_userauth_password(session, account_sftp.username, account_sftp.password)) 
		  {
			  fprintf(stderr, "Authentication by password failed.\n");
	      // stato di errore
			  state = STATE_SHUTDOWN;
			  break;
		  }
	  }
	  else 
	  {
		  int result;
		  // Or by public key 
      result = libssh2_userauth_publickey_frommemory(session,
                                            account_sftp.username,
                                            strlen( account_sftp.username),
                                           key_public,
                                           strlen(key_public),
                                           key_private,
                                           strlen(key_private),
                                           account_sftp.passphrase);
      if (result != 0)
      {
                          fprintf (stderr, "Authentication by public key failed.\n");
                          // stato di errore
                          state = STATE_SHUTDOWN;
                          break;
      }
    }
	state = 21;
	break;
	  	
case 21:
     // gestione di file json
	if (fl_APP_ID_TRANSMIT_ID == true)
	{
		int answer;
		fl_APP_ID_TRANSMIT_ID = false;
//    CheckFreeSpaceOnHeap();
//    printf ("----------------------------------------\n\r");
//    
    ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_ALL) );

        // WRITE file to server sftp
		answer = InvioJsonIdPowerOnIdentification(session);

//    ESP_ERROR_CHECK( heap_trace_stop() );
//    heap_trace_dump();

//    CheckFreeSpaceOnHeap();
//    printf ("----------------------------------------\n\r");
//    printf ("----------------------------------------\n\r");
//    printf (" ");
  }
	else if (fl_NEW_TRAVEL_NUMBER_ID == true)
	{
		int answer;
		fl_NEW_TRAVEL_NUMBER_ID = false;
		answer = InvioJsonIdNewTravelNumber(session);
	}
	else if (fl_CHECK_TRAVEL_NUMBER_ID == true)
	{
		int answer;
		fl_CHECK_TRAVEL_NUMBER_ID = false;
		answer = SearchJsonIdNewTravelNumber(session);
	}
	else if (fl_CHECK_CODE_DISCOUNT_ID == true)
	{
		int answer;

    // ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_ALL) );
		fl_CHECK_CODE_DISCOUNT_ID = false;
		answer = SearchJsonIdCodeDiscount(session);
  } 
	else if (fl_NEW_DELIVERY_ID == true)
	{
		int answer;
		fl_NEW_DELIVERY_ID = false;
        // WRITE file to server sftp
		answer = InvioJsonDelivery(session);
	}
		  
    state = STATE_SHUTDOWN;
    break;
	  
case STATE_SHUTDOWN:
    if (session)
    {
      libssh2_session_disconnect (session, "Normal Shutdown");
      libssh2_session_free (session);
	  session = NULL;
    }

    if (sock != LIBSSH2_INVALID_SOCKET)
    {
      shutdown (sock, 2);
      close (sock);
    }

    // fprintf (stderr, "all done\n");
    libssh2_exit ( );
    
    state = STATE_IDLE;
    break;

case STATE_IDLE:
    // stato di idle si attende che gli stati permettono l'attivazione del client sftp
    if ( (app_Machine.SftpServerSetup.SoloServerSftp != 0) && (chunk_operation_running == false) )
    {
      state = 0;
    }
    vTaskDelay (10);
    break;
   }
}
//-----------------------------------------------------------------------------


More information about the libssh2-devel mailing list