Mbedtls Adding Receive Timeouts

In my program sometimes I am finding that inside MQTT_ProcessLoop I am getting blocked. This is a very difficult block to catch as it only happens maybe 1% of the time. I believe the block is happening here
MQTT_ProcessLoop > receiveSingleIteration > MQTT_GetIncomingPacketTypeAndLength > readFunc

This makes me believe that this is happening inside of mbedtls_ssl_read. Upon inspection, it seems like mbedtls_ssl_read is a blocking function by default. I am trying to add a timeout to mbedtls_ssl_read, but it is causing my handshaking to fail. Here’s some snippets from my attempt.

I’m wondering if I am calling the callback function in the incorrect location of TLS_setup?

/**
 * \brief          Callback type: set a pair of timers/delays to watch
 *
 * \param ctx      Context pointer
 * \param int_ms   Intermediate delay in milliseconds
 * \param fin_ms   Final delay in milliseconds
 *                 0 cancels the current timer.
 *
 * \note           This callback must at least store the necessary information
 *                 for the associated \c mbedtls_ssl_get_timer_t callback to
 *                 return correct information.
 *
 * \note           If using a event-driven style of programming, an event must
 *                 be generated when the final delay is passed. The event must
 *                 cause a call to \c mbedtls_ssl_handshake() with the proper
 *                 SSL context to be scheduled. Care must be taken to ensure
 *                 that at most one such call happens at a time.
 *
 * \note           Only one timer at a time must be running. Calling this
 *                 function while a timer is running must cancel it. Cancelled
 *                 timers must not generate any event.
 */
void TLS_set_timer( void * ctx, uint32_t int_ms, uint32_t fin_ms )

{
    U8 timerStatus = true;
    timerSSL_t * timer = ctx;
    U32 currentTime = TLS_GetTimeMsCallback();

    if( !timer )
    {
        // LOG_error(("Timer not found"));
        timerStatus = false;
    }

    if( timerStatus )
    {
        if( int_ms == 0 || fin_ms == 0 )
        {
            // LOG_debug(("Timer canceled"));
            timer->isRunning = false;
        }
        else
        {   
            // LOG_debug(("Timer set"));
            timer->start_ms = currentTime;
            timer->int_ms = currentTime + int_ms;
            timer->fin_ms = currentTime + fin_ms;
            timer->isRunning = true;
        }   
    }
}

/**
 * \brief          Callback type: get status of timers/delays
 *
 * \param ctx      Context pointer
 *
 * \return         This callback must return:
 *                 -1 if cancelled (fin_ms == 0),
 *                  0 if none of the delays have passed,
 *                  1 if only the intermediate delay has passed,
 *                  2 if the final delay has passed.
 */
int TLS_get_timer( void * ctx )

{
    int timerStatus = 0;
    timerSSL_t * timer = ctx;
    U32 currentTime = TLS_GetTimeMsCallback();

    if( !timer )
    {
        // LOG_error(("Timer not found"));
        timerStatus = -1;
    } 
    else if( timer->fin_ms == 0 )
    {
        timerStatus = -1;
    }
    else 
    {
        if( timer->int_ms >= currentTime )
        {
            timerStatus++;
        }
        if( timer->fin_ms >= currentTime )
        {
            timerStatus++;
        }
    }
    
    // LOG_error(("Timer status: %d", timerStatus));

    return timerStatus;
}
/**
 * @brief Setup SSL connection with server
 * 
 * @param pNetworkContext 
 * @param pSslContext Secured connection context
 * @param pNetworkCredentials Contains the credentials necessary for tls connection setup
 * @return int32_t Error code
 */
static int32_t SSL_setup( NetworkContext_t * pNetworkContext, SSLContext_t * pSslContext, 
                          NetworkCredentials_t *pNetworkCredentials )

{
    Status.ErrorsIOT |= IOT_SSL_SETUP;

    int32_t mbedtlsError = TLS_TRANSPORT_SUCCESS;

    LOG_debug( ( "Initializing SSL" ) );

    mbedtls_ssl_config_init( &( pSslContext->config ) );
    mbedtls_x509_crt_init( &( pSslContext->rootCa ) );                                  // Root Server Cert
    mbedtls_ssl_init( &( pSslContext->context ) );
    mbedtls_ctr_drbg_init( &ctr_drbg );                                                 // Random Number Generation
    mbedtls_entropy_init( &entropy );                                                   // Random Number Generation 
    
    // mbedtls_ssl_conf_dbg(&(pSslContext->config), debugWrapper, NULL);
    mbedtls_ssl_set_timer_cb(pSslContext, &timerSSL, TLS_set_timer, TLS_get_timer);

    mbedtlsError = xInitializePkcs11Session( &( pSslContext->xP11Session ) );           // Holds callback functions 
    
    if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
    {
        return TLS_TRANSPORT_INTERNAL_ERROR;
    }
                       
    mbedtlsError = C_GetFunctionList( &( pSslContext->pxP11FunctionList ) );            // Holds callback functions

    if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
    {
        return TLS_TRANSPORT_INTERNAL_ERROR;
    }

    mbedtlsError =  mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 );                            // Seed for RNG

    if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
    {
        return TLS_TRANSPORT_INTERNAL_ERROR;
    }

    mbedtlsError = mbedtls_ssl_config_defaults( &(pSslContext->config), MBEDTLS_SSL_IS_CLIENT,
                                                MBEDTLS_SSL_TRANSPORT_STREAM,
                                                MBEDTLS_SSL_PRESET_DEFAULT );

    if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
    {
        return TLS_TRANSPORT_INSUFFICIENT_MEMORY;
    }

    pSslContext->certProfile = mbedtls_x509_crt_profile_default;                        // Root server certificate
    
    mbedtls_ssl_conf_authmode(&(pSslContext->config), MBEDTLS_SSL_VERIFY_REQUIRED);

    LOG_debug(("Setting TRNG function"));
    mbedtls_ssl_conf_rng(&(pSslContext->config), callbackRandomBytes, pSslContext);

    LOG_debug(("Checking root server certificate"));
    mbedtls_ssl_conf_cert_profile(&(pSslContext->config), &(pSslContext->certProfile));

    /* Parse the server root CA certificate into the SSL context. */
    mbedtlsError = mbedtls_x509_crt_parse(&(pSslContext->rootCa), pNetworkCredentials->pRootCa,
                                            pNetworkCredentials->rootCaSize);
    
    mbedtls_ssl_conf_ca_chain(&(pSslContext->config), &(pSslContext->rootCa), NULL);

    if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
    {
        return TLS_TRANSPORT_INVALID_CREDENTIALS;
    }

    /* Enable SNI if requested. */
    if( pNetworkCredentials->disableSni == pdFALSE )
    {
        LOG_debug( ( "Checking hostname" ) );
        mbedtlsError = mbedtls_ssl_set_hostname(&(pSslContext->context), pNetworkContext->hostname);

        if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
        {
            return TLS_TRANSPORT_INTERNAL_ERROR;
        }
    }

    /* Set Maximum Fragment Length if enabled. */
    #ifdef MBEDTLS_SSL_MAX_FRAGMENT_LENGTH

        /* Enable the max fragment extension. 4096 bytes is currently the largest fragment size permitted.
        * See RFC 8449 https://tools.ietf.org/html/rfc8449 for more information.
        *
        * Smaller values can be found in "mbedtls/include/ssl.h".
        */
        mbedtlsError = mbedtls_ssl_conf_max_frag_len(&(pTlsTransportParams->sslContext.config), 
                                                     MBEDTLS_SSL_MAX_FRAG_LEN_4096 );

        if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
        {
            return TLS_TRANSPORT_INTERNAL_ERROR;
        }

    #endif /* ifdef MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */

    Status.ErrorsIOT &= ~IOT_SSL_SETUP;

    return TLS_TRANSPORT_SUCCESS;
}

/**
 * @brief Performs the SSL handshake with the server
 * 
 * @param pSslContext Secured connection context
 * @param pTlsTransportParams Definition of the network context for the transport interface 
 * implementation that uses mbedTLS and FreeRTOS+TLS sockets
 * @return int32_t 
 */
static int32_t SSL_handshake( SSLContext_t * pSslContext, 
                              TlsTransportParams_t * pTlsTransportParams ) 

{
    Status.ErrorsIOT |= IOT_SSL_HANDSHAKE;

    int32_t mbedtlsError = TLS_TRANSPORT_SUCCESS;
    
    LOG_debug( ( "Attempting TLS handshake" ) );

    mbedtlsError = mbedtls_ssl_setup(&(pSslContext->context), &(pSslContext->config));
    
    if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
    {
        return mbedtlsError;
    }

    /* These two macros MBEDTLS_SSL_SEND and MBEDTLS_SSL_RECV need to be
        * defined in mbedtls_config.h according to which implementation you use.
        */
    mbedtls_ssl_set_bio(&( pSslContext->context ), (void *) pTlsTransportParams->tcpSocket,
                        mbedtls_platform_send, mbedtls_platform_recv, NULL);
    
    do
    {
        LOG_debug( ( "Shaking hands" ) );
        mbedtlsError = mbedtls_ssl_handshake( &( pSslContext->context ) );
    } while( ( mbedtlsError == MBEDTLS_ERR_SSL_WANT_READ ) ||
             ( mbedtlsError == MBEDTLS_ERR_SSL_WANT_WRITE ) );

    if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
    {
        return TLS_TRANSPORT_HANDSHAKE_FAILED;
    }

    Status.ErrorsIOT &= ~IOT_SSL_HANDSHAKE;

    return TLS_TRANSPORT_SUCCESS;
}

Can you try adding the timeout after the handshake is complete?

Thank-you for the reply. I tried it after the handshake and it began to work. I realized I was handing the wrong pointer into the mbedtls_ssl_set_timer_cb function upon closer inspection. Seems to be working properly now. Thank-you for the help!

Thank you for reporting back.