0

Been running valgrind on my code the last few days and one that I can not figure out is on the first write to libwebsockets and only the first write valgrind gives me this cryptic branching off of uninitialized variable. After the first write valgrind no longer complains of this issue. Is there something missing in LWS init function shown below? Or is this normal for LWS?

2021-10-25T13:46:56-04:00 : Write:{"protocol": "json", "version": 1 } 37
==911== Thread 6:
==911== Conditional jump or move depends on uninitialised value(s)
==911==    at 0x4886530: ??? (in /usr/lib/libwebsockets.so.15)
==911==    by 0x48901A3: ??? (in /usr/lib/libwebsockets.so.15)
==911==    by 0x488678F: lws_write (in /usr/lib/libwebsockets.so.15)
==911==    by 0x1582BB: HostComm::SocketComm::socketComm::lwsClientWriteableHandler(lws*) (socketComm.cpp:1896)
==911==    by 0x1575B7: HostComm::SocketComm::socketComm::internalWebsocketStateMachine(lws*, lws_callback_reasons, void*, void*, unsigned long) (socketComm.cpp:1640)
==911==    by 0x159DAB: HostComm::SocketComm::socketComm::websocketsStateMachine(lws*, lws_callback_reasons, void*, void*, unsigned long) (socketComm.h:431)
==911==    by 0x488AA77: ??? (in /usr/lib/libwebsockets.so.15)
==911==    by 0x4889913: lws_handle_POLLOUT_event (in /usr/lib/libwebsockets.so.15)
==911==    by 0x48905EF: ??? (in /usr/lib/libwebsockets.so.15)
==911==    by 0x4889FC3: lws_service_fd_tsi (in /usr/lib/libwebsockets.so.15)
==911==    by 0x48A2B83: ??? (in /usr/lib/libwebsockets.so.15)
==911==    by 0x488A0E3: lws_service (in /usr/lib/libwebsockets.so.15)
==911==
==911== (action on error) vgdb me ...

Here is the init routine.

/**
 * \brief Initiates the WS connection.
 * \return Returns 0 on success otherwise -1
 */
int socketComm::connectWS( const char * url, int port )
{
    int retValue = ERROR_NONE;

    char path[PATH_MAX_SIZE] = { 0 };

    if ( url != nullptr )
    {
        this->_lwsCloseRequested = false;
        this->_lwsStarted = false;
        this->_httpStarted = false;
        if( this->_lwsInit == false )
        {
            // Memset both ctx creation and client info
            memset( &this->_wsCtxCreationInfo, 0, sizeof( this->_wsCtxCreationInfo ) );
            memset( &this->_wsClientConnectInfo, 0, sizeof( this->_wsClientConnectInfo ) );

            this->_wsClientConnectInfo.port = port;
            this->_wsClientConnectInfo.address = url;
            ( void ) strncpy( path, this->_serverPath, ( PATH_MAX_SIZE - 1 ) );
            ( void ) strncat( path, this->_connectionID.c_str( ), ( PATH_MAX_SIZE - 1 - strlen( path ) ) );
            this->_wsClientConnectInfo.path = path;
            if ( _use_wss == false )
            {
                // Don't use SSL for this connection
                this->_wsClientConnectInfo.ssl_connection = 0;
            }
            else
            {
                // Use SSL for this connection
                this->_wsClientConnectInfo.ssl_connection = 1;
            }

            // Set up the context creation info
            // We don't want this client to listen
            this->_wsCtxCreationInfo.port = CONTEXT_PORT_NO_LISTEN;

            // Use our protocol list
            this->_wsCtxCreationInfo.protocols = this->_protocols;
            // Set the gid and uid to -1, isn't used much
            this->_wsCtxCreationInfo.gid = -1;
            this->_wsCtxCreationInfo.uid = -1;

            // Use our extensions list
            this->_wsCtxCreationInfo.extensions = _extensions;
            // Pass a copy of our class to be used in the callback.
            this->_wsCtxCreationInfo.user = static_cast<void*>(this);

            // Create the context with the info
            this->_wsCtx = lws_create_context( &this->_wsCtxCreationInfo );
            if ( this->_wsCtx == nullptr )
            {
                this->setInternalError( CREATE_ERROR( SOCK_ERR_CTX ) );
                retValue = -1;
            }
            else
            {
                // Set up the client creation info
                // Use our created context
                this->_wsClientConnectInfo.context = this->_wsCtx;

                // Set the connections host to the address
                this->_wsClientConnectInfo.host = this->_wsClientConnectInfo.address;

                // Set the connections origin to the address
                this->_wsClientConnectInfo.origin = this->_wsClientConnectInfo.address;

                // IETF version is -1 (the latest one)
                this->_wsClientConnectInfo.ietf_version_or_minus_one = -1;

                // which protocol to use.
                this->_wsClientConnectInfo.protocol = this->_protocols[PROTOCOL_WS].name;

                // The created client should be fed inside the wsi_test variable
                this->_wsClientConnectInfo.pwsi = &this->_wsInterface;
#if( SOCKET_DEBUG == 1)
                string msg;
                msg = string( "Connecting to " ) + string( "ws://" )  + this->_wsClientConnectInfo.address;
                msg += ":" + to_string( this->_wsClientConnectInfo.port );
                LOG_DEBUG( msg );
#endif
                this->_lwsInit = true;
            }
        }

        // If we are already inited then go head and connect.
        if( ( this->_lwsInit == true ) && ( this->_wsCtx != nullptr ) )
        {
            // Connect with the client info
            lws_client_connect_via_info( &this->_wsClientConnectInfo );
            if ( this->_wsInterface == nullptr )
            {
                // Log and store an internal error.
                this->setInternalError( CREATE_ERROR( SOCK_ERR_CONNECT ) );
                retValue = -1;
            }
            else
            {
                // Make sure that LWS service is doing things.
                this->setTimer( 500, false );
                this->_lwsStarted = true;
            }
        }
        else
        {
            // Log and store an internal error.
            this->setInternalError( CREATE_ERROR( SOCK_ERR_CONNECT ) );
            retValue = -1;
        }
    }
    else
    {
        this->setInternalError( CREATE_ERROR( NULL_PTR_ERROR ) );
        retValue = -1;
    }
    return ( retValue );
}

Here is the LWS callback

/**
 * \brief Websocket state machine used by LWS to handle the events coming out of
 * the library.
 *
 * \returns An error if one occurred during the connection.
 */
int socketComm::internalWebsocketStateMachine( struct lws* wsi,
                                               lws_callback_reasons reason,
                                               void *user, void* in, size_t len )
{
    int retValue = ERROR_NONE;
    // The buffer holding the data to send
    // NOTICE: data which is sent always needs to have a certain amount of memory (LWS_PRE) preserved for headers
    char * bufDataStart = &this->_rxBuf[LWS_PRE];
    int count = 0;
    int copySize = MAX_MSG_SIZE;

    std::vector<uint8_t>  rxMsg;
#if( SOCKET_DEBUG == 1 )
    string infomsg;
    // The socket is writable quite often
    if( ( reason != LWS_CALLBACK_CLIENT_WRITEABLE ) && ( reason != LWS_CALLBACK_TIMER ) )
    {
        LOG_DEBUG( to_string( reason ) );
    }
#endif
    if( wsi != nullptr )
    {
        // For which reason was this callback called?
        switch ( reason )
        {
            // Handles errors in both connections
            case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: // 1
                retValue = this->lwsClientConnectionErrorHandler( wsi,
                                                                  in );
            break;

            // The connection was successfully established
            case LWS_CALLBACK_CLIENT_ESTABLISHED: // 3
    #if( SOCKET_DEBUG == 1 )
                LOG_DEBUG( "[PROTOCOL_WS] Connection to server established.\n" );
    #endif
                // Tell LWS to callback on writable
                lws_callback_on_writable( wsi );
            break;

            // The connection closed
            case LWS_CALLBACK_CLOSED: // 4
    #if( SOCKET_DEBUG == 1 )
                LOG_DEBUG( "[PROTOCOL_WS] Connection closed.\n" );
    #endif
            break;

            // Our client received something Websockets
            case LWS_CALLBACK_CLIENT_RECEIVE: // 8
                retValue = this->lwsClientReceiveHandler( in, len );
            break;

            // The server notifies us that we can write data
            case LWS_CALLBACK_CLIENT_WRITEABLE: // 10
                // Handle writable
                retValue = this->lwsClientWriteableHandler( wsi );
            break;

            // Used by our HTTP client to tell the server we are sending json and a length of 0
            case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: // 24
                retValue = this->lwsHTTPHandshakeHandler( wsi,
                                                          reason,
                                                          user,
                                                          in,
                                                          len );
            break;
            // last call where a wsi is valid
            case LWS_CALLBACK_WSI_DESTROY:
                // Protection to make sure we are not servicing a null.
               if( wsi == this->_wsInterface )
               {
                   this->_lwsStarted = false;
               }
               else
               {
                   if( wsi == this->_httpInterface )
                   {
                       this->_httpStarted = false;
                   }
               }
            break;
            // Response status to a post.
            case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:   // 44
                // This is the HTTP Status.
                this->_httpStatus = ( int ) lws_http_client_http_response( wsi );
    #if( SOCKET_DEBUG == 1 )
                LOG_DEBUG("HTTP_Connected with server response: " + to_string( ( int )lws_http_client_http_response( wsi ) ) );
    #endif
                retValue = lws_callback_http_dummy( wsi, reason, user, in, len );
            break;

            case LWS_CALLBACK_CLOSED_CLIENT_HTTP:  // 45
               retValue = this->lwsHTTPCloseHandler( wsi,
                                                     reason,
                                                     user,
                                                     in,
                                                     len );
            break;

            // Callback telling us there might be something in the buffer.
            case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: // 46
    #if( SOCKET_DEBUG == 1 )
                LOG_DEBUG( "LWS_CALLBACK_RECEIVE_CLIENT_HTTP" );
    #endif
                // This is a call back saying that we need to read. Should read the
                // Max amount then when the callback happens it will give an actual
                // Length.
                this->_count = MAX_MSG_SIZE;
                (void)memset( bufDataStart, 0, this->_count );
                retValue = lws_http_client_read( wsi, &bufDataStart, &this->_count );

            break;

            // HTTP done
            case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: // 47
    #if( SOCKET_DEBUG == 1 )
                LOG_DEBUG( "LWS_CALLBACK_COMPLETED_CLIENT_HTTP" );
    #endif
                // Because we are only sending the one message we need to cancel servicing this
                // context.
                lws_cancel_service( lws_get_context( wsi ) );
                retValue = lws_callback_http_dummy( wsi, reason, user, in, len );
            break;

            /* Call back that actually reads */
            case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: // 48
    #if( SOCKET_DEBUG == 1 )
                // Read the data and push it onto our rx buffer.
                LOG_DEBUG( "Negotiate RECEIVE_CLIENT_HTTP_READ: read " + to_string( len ) );
    #endif
                if( ( len > 0 ) && ( in !=  nullptr ) )
                {
                    rxMsg.assign( (uint8_t*)in, &((uint8_t*)in)[len] );
                    this->addRXMsg( rxMsg, &count );
                }
                retValue = lws_callback_http_dummy( wsi, reason, user, in, len );
            break;

            case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:  // 57
    #if( SOCKET_DEBUG == 1 )
                LOG_DEBUG( "LWS_CALLBACK_CLIENT_HTTP_WRITEABLE" );
    #endif
                // We do not write body data in this state machine
                retValue = lws_callback_http_dummy( wsi, reason, user, in, len );

            break;

            case LWS_CALLBACK_TIMER: // 73
                if( this->_lwsStarted == true )
                {
                    this->setTimer( 500, false );
                }
                else
                {
                    if( this->_httpStarted == true )
                    {
                        this->setTimer( 500, true );
                    }
                }

            break;

            case LWS_CALLBACK_CLIENT_CLOSED: // 75
                retValue = internalSendNotify( COMM_SOCKET_WS_DISCONNECTED );
            break;

            default:
                /*
                 * We can get extra callbacks here, if nothing to do,
                 * then do nothing.
                 */
            break;
        }
    }

    if( this->_lwsCloseRequested == true )
    {
        retValue = -1;
        this->_lwsCloseRequested = false;
    }

    return ( retValue );
}

Here is the specific function that was calling the write routine.

/**
 * \brief Handler for LWS client receive message.
 *
 * \returns An error if one occurred during the operation.
 */
int socketComm::lwsClientWriteableHandler( struct lws * wsi )
{
    std::vector<uint8_t>  txMsg;
    int messageCount = 0;
    // The buffer holding the data to send
    // NOTICE: data which is sent always needs to have a certain amount of memory (LWS_PRE) preserved for headers
    memset( this->_txBuf, 0, sizeof( this->_txBuf ) );
    // See if there is a message on the buffer.
    int retValue = this->getTxMsg( &txMsg, &messageCount );
    if( messageCount >= 0 )
    {
        if( txMsg.size( ) > 0 )
        {
        // Lets try not to blow stacks
            int copySize = ( txMsg.size() > ( MAX_MSG_SIZE - 1 ) ) ? ( MAX_MSG_SIZE - 1 ) : txMsg.size();

            /* copy our data over to the send buffer and send it out */
            (void)memcpy( &this->_txBuf[LWS_PRE], txMsg.data(), copySize );
    #if( SOCKET_DEBUG == 1 )
            std::string infomsg =  string("Write:") + string(&this->_txBuf[LWS_PRE]) + string(" ") + to_string( copySize );
            // Only log small messages.
            if( copySize < 3048 )
            {
                LOG_DEBUG( infomsg );
            }
    #endif
            if( wsi != nullptr )
            {
                // Write the buffer from the LWS_PRE index + 128 (the buffer size)
                int numBytes = lws_write( wsi,
                                          (unsigned char *)&(this->_txBuf[LWS_PRE]),
                                          copySize,
                                          LWS_WRITE_TEXT);

                if( ( messageCount > 0 ) && ( wsi != nullptr ) )
                {
                    // Tell websockets we have more messages to send.
                    lws_callback_on_writable( wsi );
                }
            }
        }
    }
    return( retValue );
}

Output when track-origins is enabled


==1799== Thread 6:
==1799== Conditional jump or move depends on uninitialised value(s)
==1799==    at 0x4886530: ??? (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x48901A3: ??? (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x488678F: lws_write (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x158A33: HostComm::SocketComm::socketComm::lwsClientWriteableHandler(lws*) (socketComm.cpp:1912)
==1799==    by 0x157CDF: HostComm::SocketComm::socketComm::internalWebsocketStateMachine(lws*, lws_callback_reasons, void*, void*, unsigned long) (socketComm.cpp:1646)
==1799==    by 0x15A5BF: HostComm::SocketComm::socketComm::websocketsStateMachine(lws*, lws_callback_reasons, void*, void*, unsigned long) (socketComm.h:431)
==1799==    by 0x488AA77: ??? (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x4889913: lws_handle_POLLOUT_event (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x48905EF: ??? (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x4889FC3: lws_service_fd_tsi (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x48A2B83: ??? (in /usr/lib/libwebsockets.so.15)
==1799==    by 0x488A0E3: lws_service (in /usr/lib/libwebsockets.so.15)
==1799==  Uninitialised value was created by a heap allocation
==1799==    at 0x4848414: operator new(unsigned long) (vg_replace_malloc.c:334)
==1799==    by 0x12109F: HostComm::hostComm::hostComm() (hostComm.cpp:46)
==1799==    by 0x192597: SM::sysMgr::sysMgr() (sysMgr.cpp:42)
==1799==    by 0x19ECEF: __static_initialization_and_destruction_0(int, int) (sysMgr.cpp:34)
==1799==    by 0x19EFE7: _GLOBAL__sub_I__ZN2SM5smMgrE (sysMgr.cpp:3740)
==1799==    by 0x1AE50F: __libc_csu_init (elf-init.c:88)
==1799==    by 0x5E4435B: (below main) (libc-start.c:264)
==1799==
==1799== (action on error) vgdb me ...
==1799== Continuing ...
2021-10-26T09:15:07-04:00 : 8
tbandtg
  • 9
  • 2
  • The only thing it could possibly be complaining about is `socketComm::_wsInterface`. How is that initialized? – Tim Roberts Oct 25 '21 at 18:10
  • 1
    It's not impossible that this is a bug in libwebsockets itself. Try linking with a debug version so that you get the exact source code location of the uninitialized read. Even if the bug turns out to be in your own code, this might provide a clue where it is. – Thomas Oct 25 '21 at 18:13
  • If you have not used it, the option --track-origins=yes might help to point at the problem – phd Oct 25 '21 at 20:15
  • _wsInterface is intitialized by the connect with context command. I will run it with trackOrigins=yes next. – tbandtg Oct 26 '21 at 13:07
  • https://gist.github.com/iUltimateLP/17604e35f0d7a859c7a263075581f99a Is the example I used to setup the websocket client. – tbandtg Oct 26 '21 at 13:09
  • I added output from track-origins to the bottom – tbandtg Oct 26 '21 at 13:17

0 Answers0