libssh2 / libssh2

the SSH library

Home Page:https://libssh2.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

After running this code, the memory usage continuously spikes. What could be the reason? libssh2-1.10.0

xpston008 opened this issue · comments

libssh2-1.10.0

#include <libssh2.h>
#include <libssh2_sftp.h>

#include <iostream>
#include <cstring>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "libssh2.lib")

class SFTPClient {
public:
    SFTPClient(const std::string &hostname, int port, const std::string &username, const std::string &password)
        : hostname(hostname), port(port), username(username), password(password), sock(INVALID_SOCKET), session(nullptr), sftp_session(nullptr) {
    }

    ~SFTPClient() {
        disconnect();
    }

    bool WaitConnect()
    {
        struct timeval tv;
        tv.tv_sec = 10;
        tv.tv_usec = 0;

        fd_set write, err;
        FD_ZERO(&write);
        FD_ZERO(&err);
        FD_SET(sock, &write);
        FD_SET(sock, &err);

        // check if the socket is ready

        int rc = select(sock + 1, NULL, &write, &err, &tv);

        switch (rc)
        {
        case 0:
            std::cout << ("connect server timeout");
        case -1:
            std::cout << ("connect server select (%d)", errno);
        default:
            break;
        }

        if (!FD_ISSET(sock, &write))
        {
            std::cout << ("select write socket failed: %d", errno);
        }
        int error = WSAEINPROGRESS;
        int len = sizeof(int);
        getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)(&error), (socklen_t*)&len);
        if (error)
        {
            std::cout << ("connect WSAEINPROGRESS getsockopt[SO_ERROR] : %d");
        }
        return true;
    }

    bool connect() {

        // Initialize libssh2
        if (libssh2_init(0) != 0) {
            std::cerr << "libssh2 initialization failed" << std::endl;
            return false;
        }

        /// Create socket
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == INVALID_SOCKET) {
            std::cerr << "Failed to create socket" << std::endl;
            return false;
        }

        // Set socket to non-blocking mode
        u_long mode = 1; // Non-blocking connect
        if (ioctlsocket(sock, FIONBIO, &mode) != NO_ERROR) {
            std::cerr << "Failed to set socket non-blocking" << std::endl;
            return false;
        }

        sockaddr_in sin{};
        sin.sin_family = AF_INET;
        sin.sin_port = htons(port);
        inet_pton(AF_INET, hostname.c_str(), &sin.sin_addr);

        // Non-blocking connect
        if (::connect(sock, (sockaddr*)(&sin), sizeof(sin)) == SOCKET_ERROR) {
            if (WSAGetLastError() != WSAEWOULDBLOCK) {
                std::cerr << "Failed to connect" << std::endl;
                return false;
            }
            WaitConnect();
        }

        // Create an SSH session
        session = libssh2_session_init();
        if (!session) {
            std::cerr << "Failed to create libssh2 session" << std::endl;
            return false;
        }

        libssh2_session_set_blocking(session, 0); // Set session to non-blocking mode

                                                  // Perform SSH session handshake
        int rc;
        do {
            rc = libssh2_session_handshake(session, sock);
        } while (rc == LIBSSH2_ERROR_EAGAIN);
        if (rc) {
            std::cerr << "Failure establishing SSH session: " << rc << std::endl;
            return false;
        }

        // User authentication
        do {
            rc = libssh2_userauth_password(session, username.c_str(), password.c_str());
        } while (rc == LIBSSH2_ERROR_EAGAIN);
        if (rc) {
            std::cerr << "Authentication by password failed." << std::endl;
            return false;
        }

        // Initialize SFTP session
        do {
            sftp_session = libssh2_sftp_init(session);
        } while (!sftp_session && libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN);
        if (!sftp_session) {
            std::cerr << "Unable to init SFTP session" << std::endl;
            return false;
        }

        return true;
    }

    void disconnect() {
        if (sftp_session) {
            libssh2_sftp_shutdown(sftp_session);
            sftp_session = nullptr;
        }
        if (session) {
            libssh2_session_disconnect(session, "Normal Shutdown");
            libssh2_session_free(session);
            session = nullptr;
        }
        if (sock != INVALID_SOCKET) {
            closesocket(sock);
            sock = INVALID_SOCKET;
        }
        libssh2_exit();
        //WSACleanup();
    }

private:
    std::string hostname;
    int port;
    std::string username;
    std::string password;

    SOCKET sock;
    LIBSSH2_SESSION *session;
    LIBSSH2_SFTP *sftp_session;
};
int main() {
#ifdef WIN32
    WSADATA wsadata;
    int err;

    err = WSAStartup(MAKEWORD(2, 0), &wsadata);
    if (err != 0) {
        fprintf(stderr, "WSAStartup failed with error: %d\n", err);
        return 1;
    }
#endif

    while (1)
    {
        SFTPClient client("192.168.2.121", 22, "abc", "abc123.501");
        if (client.connect()) {
            std::cout << "Connected successfully" << std::endl;
            // More SFTP operations can be added here, such as uploading, downloading files, etc.
        }
        else {
            std::cerr << "Failed to connect" << std::endl;
        }
    }

    return 0;
}

@dfandrich @vszakats

Moving libssh2_init outside of the while loop, the memory still keeps increasing, which is very strange.
WSACleanup does not need to be called with every connect.

I don't know all the Windows API details, but the call to WSACleanup is commented out in the example code. I believe WSAStartup only needs to be called once at program initialization rather than calling WSAStartup/WSACleanup for every connection. Similarly, libssh2_init() should also only be called once, not for every session.

Yes, it has been moved out of the loop.
Previously, when working on numerous Windows socket programs, WSAStartup() only needed to be initialized once.

By changing this piece of code to blocking mode, memory usage will not surge, which is quite peculiar.
@dfandrich

@bagder @vszakats @mback2k @willco007
@dfandrich

Could you please help explain what I need to do? Thank you!

/*session.c */
#include <stdio.h>
unsigned alloc_counts = 0;
unsigned free_counts = 0;
/* libssh2_default_alloc
 */
static
LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
{
    printf("alloc = %d\n", ++alloc_counts);
    (void) abstract;
    return malloc(count);
}

/* libssh2_default_free
 */
static
LIBSSH2_FREE_FUNC(libssh2_default_free)
{
    printf("free = %d\n", ++free_counts);
    (void) abstract;
    free(ptr);
}

/* libssh2_default_realloc
 */

After modifying the session.c file and compiling it
I added a counter named alloc_counts to LIBSSH2_ALLOC_FUNC and a counter named free_counts to LIBSSH2_FREE_FUNC.

When executing the following code and printing the results to the screen, it was observed that the number of memory allocations and memory releases is not equal. alloc_counts=43, free_counts=32.

Does libssh2 have a memory leak issue?

#include <libssh2.h>
#include <libssh2_sftp.h>
#include <iostream>
#include <cstring>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>

class SFTPClient {
public:
    SFTPClient(const std::string &hostname, int port, const std::string &username, const std::string &password)
        : hostname(hostname), port(port), username(username), password(password), sock(INVALID_SOCKET), session(nullptr), sftp_session(nullptr) {
    }

    ~SFTPClient() {
        disconnect();
    }

    bool WaitConnect()
    {
        struct timeval tv;
        tv.tv_sec = 10;
        tv.tv_usec = 0;

        fd_set write, err;
        FD_ZERO(&write);
        FD_ZERO(&err);
        FD_SET(sock, &write);
        FD_SET(sock, &err);

        // check if the socket is ready

        int rc = select(sock + 1, NULL, &write, &err, &tv);

        switch (rc)
        {
        case 0:
            std::cout << ("connect server timeout");
        case -1:
            std::cout << ("connect server select (%d)", errno);
        default:
            break;
        }

        if (!FD_ISSET(sock, &write))
        {
            std::cout << ("select write socket failed: %d", errno);
        }
        int error = WSAEINPROGRESS;
        int len = sizeof(int);
        getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)(&error), (socklen_t*)&len);
        if (error)
        {
            std::cout << ("connect WSAEINPROGRESS getsockopt[SO_ERROR] : %d");
        }
        return true;
    }

    bool connect() {



        /// Create socket
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == INVALID_SOCKET) {
            std::cerr << "Failed to create socket" << std::endl;
            return false;
        }

        // Set socket to non-blocking mode
        u_long mode = 1; // Non-blocking connect
        if (ioctlsocket(sock, FIONBIO, &mode) != NO_ERROR) {
            std::cerr << "Failed to set socket non-blocking" << std::endl;
            return false;
        }

        sockaddr_in sin{};
        sin.sin_family = AF_INET;
        sin.sin_port = htons(port);
        inet_pton(AF_INET, hostname.c_str(), &sin.sin_addr);

        // Non-blocking connect
        if (::connect(sock, (sockaddr*)(&sin), sizeof(sin)) == SOCKET_ERROR) {
            if (WSAGetLastError() != WSAEWOULDBLOCK) {
                std::cerr << "Failed to connect" << std::endl;
                return false;
            }
            WaitConnect();
        }

        // Create an SSH session
        session = libssh2_session_init();
        if (!session) {
            std::cerr << "Failed to create libssh2 session" << std::endl;
            return false;
        }

        libssh2_session_set_blocking(session, 0); // Set session to non-blocking mode

                                                  // Perform SSH session handshake
        int rc;
        do {
            rc = libssh2_session_handshake(session, sock);
        } while (rc == LIBSSH2_ERROR_EAGAIN);
        if (rc) {
            std::cerr << "Failure establishing SSH session: " << rc << std::endl;
            return false;
        }

        // User authentication
        do {
            rc = libssh2_userauth_password(session, username.c_str(), password.c_str());
        } while (rc == LIBSSH2_ERROR_EAGAIN);
        if (rc) {
            std::cerr << "Authentication by password failed." << std::endl;
            return false;
        }

        // Initialize SFTP session
        do {
            sftp_session = libssh2_sftp_init(session);
        } while (!sftp_session && libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN);
        if (!sftp_session) {
            std::cerr << "Unable to init SFTP session" << std::endl;
            return false;
        }

        return true;
    }

    void disconnect() {
        if (sftp_session) {
            libssh2_sftp_shutdown(sftp_session);
            sftp_session = nullptr;
        }
        if (session) {
            libssh2_session_disconnect(session, "Normal Shutdown");
            libssh2_session_free(session);
            session = nullptr;
        }
        if (sock != INVALID_SOCKET) {
            closesocket(sock);
            sock = INVALID_SOCKET;
        }
        //libssh2_exit();
        //WSACleanup();
    }

private:
    std::string hostname;
    int port;
    std::string username;
    std::string password;

    SOCKET sock;
    LIBSSH2_SESSION *session;
    LIBSSH2_SFTP *sftp_session;
};


#include <memory> 
void test_once_memory()
{
    std::unique_ptr<SFTPClient> client = std::make_unique<SFTPClient>("192.168.2.121", 22, "root", "root.501");
    if (client->connect()) {
        std::cout << "Connected successfully" << std::endl;
        // More SFTP operations can be added here, such as uploading, downloading files, etc.
    }
    else {
        std::cerr << "Failed to connect" << std::endl;
    }
}


int main() {

#ifdef WIN32
    WSADATA wsadata;
    int err;

    err = WSAStartup(MAKEWORD(2, 0), &wsadata);
    if (err != 0) {
        fprintf(stderr, "WSAStartup failed with error: %d\n", err);
        return 1;
    }
#endif
    // 初始化libssh2
    if (libssh2_init(0) != 0) {
        std::cerr << "libssh2 initialization failed" << std::endl;
        return false;
    }
    test_once_memory();

    return 0;
}

@dfandrich
When using the “Dr.Memory” memory monitoring tool, some memory leak issues were detected, as well as some accessible memory. The information is as follows:

ERRORS IGNORED:
      6 potential error(s) (suspected false positives)
         (details: C:\Users\anger\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.6440.000\potential_errors.txt)
     28 unique,    30 total,   7716 byte(s) of still-reachable allocation(s)
         (re-run with "-show_reachable" for details)
Details: C:\Users\anger\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.6440.000\results.txt
void disconnect() {
        if (sftp_session) {
            libssh2_sftp_shutdown(sftp_session); //"This line of code needs to be modified to non-blocking mode to avoid memory leaks."
            sftp_session = nullptr;
        }
        if (session) {
            libssh2_session_disconnect(session, "Normal Shutdown");
            libssh2_session_free(session);
            session = nullptr;
        }
        if (sock != INVALID_SOCKET) {
            closesocket(sock);
            sock = INVALID_SOCKET;
        }
        //libssh2_exit();
        //WSACleanup();
    }

"Problem resolved."
@dfandrich thanks!