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