Por ordem dos desenvolvedores incorporados: procurando bugs no Amazon FreeRTOS

Todo mundo que programa microcontroladores provavelmente conhece o FreeRTOS, ou pelo menos ouviu falar sobre esse sistema operacional. Os funcionários da Amazon decidiram expandir os recursos deste sistema operacional para trabalhar com os serviços da AWS Internet of Things - foi assim que o Amazon FreeRTOS apareceu. Nós, desenvolvedores do analisador de código PVS-Studio, fomos solicitados a verificar esses projetos pelo correio e nos comentários abaixo dos artigos. Bem, você perguntou - nós fizemos. O que veio disso - continue lendo.

Figura 3

Um pouco sobre projetos


Para começar, vou falar um pouco sobre o "pai" do projeto verificado - FreeRTOS (você pode encontrar o código fonte aqui ). Como a Wikipedia diz, o FreeRTOS é um sistema operacional de multitarefa em tempo real para sistemas embarcados.

Foi escrito no bom e velho C, o que não é surpreendente - esse sistema operacional deve funcionar em condições típicas de microcontroladores: baixo poder de computação, uma pequena quantidade de RAM e similares. A linguagem C permite que você trabalhe com recursos em um nível baixo e com alto desempenho, por isso é o mais adequado para o desenvolvimento de um sistema operacional desse tipo.

Agora, de volta à Amazônia, que não fica parada e se desenvolve em várias áreas promissoras. Por exemplo, a Amazon está desenvolvendo o mecanismo AAA do jogo Amazon Lumberyard, que também testamos .

Uma dessas áreas é a Internet das Coisas (Internet of Things, IoT). Para desenvolver nesta área, a Amazon decidiu escrever seu próprio sistema operacional - e eles tomaram o kernel do FreeRTOS como base.

O sistema resultante - Amazon FreeRTOS - está posicionado como "fornecendo a capacidade de conectar-se com segurança ao Amazon Web Services, como AWS IoT Core ou AWS IoT Greengrass". O código fonte deste projeto está armazenado no Github.

Neste artigo, examinaremos se há erros no FreeRTOS, bem como a segurança do sistema operacional da Amazon em termos de análise de código estático.

Como foi o cheque


O código foi verificado usando uma ferramenta de busca automática de erros: analisador de código estático PVS-Studio. É capaz de detectar erros em programas escritos em C, C ++, C # e Java.

Antes de iniciar a análise, é necessário montar o projeto - para ter certeza de que tenho todas as dependências necessárias e que tudo está em ordem com o projeto. Existem várias maneiras de verificar um projeto - por exemplo, usando um sistema de monitoramento de compilação. Fiz a análise usando o plug-in do Visual Studio - é bom que os repositórios dos dois projetos tenham um conjunto de arquivos de projeto que facilitam a criação no Windows.

Tudo o que era necessário para mim era coletar os projetos para garantir que houvesse tudo o que era necessário para verificação. Em seguida, lancei a análise e pronto! - na minha frente há um relatório do analisador pronto.

As bibliotecas de terceiros incluídas nesses projetos também podem conter erros e, é claro, também podem afetar a operação do programa. No entanto, eu os excluí da análise por uma questão de pureza da narração.

Assim, os projetos são analisados, os relatórios são recebidos, erros interessantes são gravados. É hora de avançar para a análise deles!

O que esconde o FreeRTOS


Inicialmente, esperava escrever dois artigos separados: um para cada sistema operacional. Eu já esfreguei minhas mãos, preparando-me para escrever um bom artigo sobre o FreeRTOS. Antecipando a detecção de pelo menos alguns erros interessantes (como o CWE-457 ), observei ansiosamente os poucos avisos do analisador e ... e nada. Não encontrei nenhum erro interessante.

Muitos avisos emitidos pelo analisador não eram relevantes para o FreeRTOS. Por exemplo, esses avisos eram deficiências de 64 bits, como converter tamanho_t para uint32_t . Isso se deve ao fato de o FreeRTOS ter sido projetado para funcionar em dispositivos com um tamanho de ponteiro de no máximo 32 bits.

Eu verifiquei cuidadosamente todos os avisos do V1027 relacionados a conversões entre ponteiros e estruturas não relacionadas. Se estruturas redutíveis têm o mesmo alinhamento, essa conversão não é um erro. E não encontrei um único elenco perigoso!

Todos os outros lugares suspeitos estavam relacionados ao estilo de codificação ou foram equipados com um comentário explicando por que isso é feito exatamente aqui e por que isso não é um erro.

Em geral, quero entrar em contato com os desenvolvedores do FreeRTOS. Vocês são realmente ótimos! Quase nunca conhecemos projetos tão limpos e de alta qualidade como o seu. Fiquei muito satisfeito ao ler códigos limpos, arrumados e bem documentados. Tiremos o chapéu para você.

Embora não tenha encontrado nenhum erro interessante naquele dia, entendi que não iria parar por aí. Eu estava voltando para casa com uma firme convicção de que algo interessante seria encontrado na versão da Amazon 100% e que amanhã eu definitivamente coletaria erros suficientes para o artigo. Como você provavelmente adivinhou, eu estava certa.

O que oculta o Amazon FreeRTOS


A versão do sistema da Amazon acabou por ser ... para dizer o mínimo, um pouco pior. O legado do FreeRTOS permaneceu igualmente limpo, mas as novas revisões foram bastante interessantes.

Em alguns lugares, a lógica do programa foi violada, em algum lugar trabalhado incorretamente com ponteiros. Em alguns lugares, o código pode levar a um comportamento indefinido, mas em algum lugar o programador simplesmente não sabia sobre o padrão de erro que ele fez. Eu até encontrei algumas vulnerabilidades em potencial sérias.

Algo que adiei com a introdução. Vamos começar a analisar os bugs!

Violação lógica do programa


Vamos começar com as áreas problemáticas, que indicam claramente que o programa não é executado exatamente como o programador esperava. O primeiro desses locais será um trabalho suspeito com uma matriz:

/** * @brief Pool of request and associated response buffers, * handles, and configurations. */ static _requestPool_t _requestPool = { 0 }; .... static int _scheduleAsyncRequest(int reqIndex, uint32_t currentRange) { .... /* Set the user private data to use in the asynchronous callback context. */ _requestPool.pRequestDatas[reqIndex].pConnHandle = &_connHandle; _requestPool.pRequestDatas[reqIndex].pConnConfig = &_connConfig; _requestPool.pRequestDatas[reqIndex].reqNum = reqIndex; _requestPool.pRequestDatas[reqIndex].currRange = currentRange; _requestPool.pRequestDatas[reqIndex].currDownloaded = 0; _requestPool.pRequestDatas[reqIndex].numReqBytes = numReqBytes; .... _requestPool.pRequestDatas->scheduled = true; .... } 

O PVS-Studio emitiu dois avisos para este trecho de código:

  • V619 A matriz '_requestPool.pRequestDatas' está sendo utilizada como um ponteiro para um único objeto. iot_demo_https_s3_download_async.c 973
  • V574 O ponteiro '_requestPool.pRequestDatas' é usado simultaneamente como uma matriz e como um ponteiro para um único objeto. Verifique as linhas: 931, 973. iot_demo_https_s3_download_async.c 973

Apenas para o caso, deixe-me lembrá-lo: o nome da matriz é um ponteiro para seu primeiro elemento. Ou seja, se _requestPool.pRequestDatas for uma matriz de estruturas, _requestPool.pRequestDatas [i] .scheduled é o acesso ao membro agendado da i- ésima estrutura da matriz. E se você escrever _requestPool.pRequestDatas-> agendado , isso significará acesso ao membro agendado da primeira estrutura da matriz.

É o que acontece no snippet de código acima. A última linha sempre altera o valor apenas para um membro da primeira estrutura da matriz. Por si só, essa chamada já é suspeita, mas a situação aqui é ainda mais óbvia: em todo o corpo da função, o array _requestPool.pRequestDatas é acessado pelo índice, e somente no final a operação de indexação foi esquecida.

Pelo que entendi, a última linha deve ficar assim:

 _requestPool.pRequestDatas[reqIndex].scheduled = true; 

O erro a seguir está em uma função pequena, portanto, eu a darei na íntegra:

 /* Return true if the string " pcString" is found * inside the token pxTok in JSON file pcJson. */ static BaseType_t prvGGDJsoneq( const char * pcJson, const jsmntok_t * const pxTok, const char * pcString ) { uint32_t ulStringSize = ( uint32_t ) pxTok->end - ( uint32_t ) pxTok->start; BaseType_t xStatus = pdFALSE; if( pxTok->type == JSMN_STRING ) { if( ( uint32_t ) strlen( pcString ) == ulStringSize ) { if( ( int16_t ) strncmp( &pcJson[ pxTok->start ], // <= pcString, ulStringSize ) == 0 ) { xStatus = pdTRUE; } } } return xStatus; } 

PVS-Studio Warning: V642 [CWE-197] Salvar o resultado da função 'strncmp' dentro da variável do tipo 'short' é inapropriado. Os bits significativos podem ser perdidos quebrando a lógica do programa. aws_greengrass_discovery.c 637

Vamos dar uma olhada na definição da função strncmp:

 int strncmp( const char *lhs, const char *rhs, size_t count ); 

No exemplo, um resultado do tipo int , cujo tamanho é 32 bits, é convertido em uma variável do tipo int16_t . Com essa conversão "restritiva", os bits mais significativos do valor de retorno serão perdidos. Por exemplo, se a função strncmp retornar 0x00010000 , a função será perdida durante a conversão e a condição será atendida.

De fato, é estranho ver esse elenco em uma condição. Por que fazer isso se você pode comparar int normal com zero? Por outro lado, se o programador conscientemente desejava que a função às vezes retornasse verdadeira , mesmo que não devesse, então por que esse comportamento complicado não é descrito pelo comentário? Mas então isso já é um marcador. Em geral, estou inclinado a acreditar que isso é um erro. O que você acha?

Comportamento indefinido e indicadores


Agora haverá um exemplo bastante amplo. Ele oculta a desreferenciação potencial de um ponteiro nulo:

 static void _networkReceiveCallback(....) { IotHttpsReturnCode_t status = IOT_HTTPS_OK; _httpsResponse_t* pCurrentHttpsResponse = NULL; IotLink_t* pQItem = NULL; .... /* Get the response from the response queue. */ IotMutex_Lock(&(pHttpsConnection->connectionMutex)); pQItem = IotDeQueue_PeekHead(&(pHttpsConnection->respQ)); IotMutex_Unlock(&(pHttpsConnection->connectionMutex)); /* If the receive callback is invoked * and there is no response expected, * then this a violation of the HTTP/1.1 protocol. */ if (pQItem == NULL) { IotLogError(....); fatalDisconnect = true; status = IOT_HTTPS_NETWORK_ERROR; goto iotCleanup; } .... iotCleanup : /* Report errors back to the application. */ if (status != IOT_HTTPS_OK) { if ( pCurrentHttpsResponse->isAsync && pCurrentHttpsResponse->pCallbacks->errorCallback) { pCurrentHttpsResponse->pCallbacks->errorCallback(....); } pCurrentHttpsResponse->syncStatus = status; } .... } 

Aviso do PVS-Studio: V522 [CWE-690] Pode haver desreferenciação de um ponteiro nulo em potencial 'pCurrentHttpsResponse'. iot_https_client.c 1184

As desreferências de problemas estão na parte inferior se . Vamos ver o que acontece aqui.

No início da função, as variáveis ​​pCurrentHttpsResponse e pQItem são inicializadas em NULL e a variável de status é inicializada em IOT_HTTPS_OK , o que significa que tudo ocorre sem problemas.

Em seguida, é atribuído ao pQItem o valor retornado da função IotDeQueue_PeekHead , que retorna um ponteiro para o início de uma fila duplamente conectada.

O que acontece se a fila estiver vazia? Nesse caso, a função IotDeQueue_PeekHead retornará NULL :

 static inline IotLink_t* IotDeQueue_PeekHead (const IotDeQueue_t* const pQueue) { return IotListDouble_PeekHead(pQueue); } .... static inline IotLink_t* IotListDouble_PeekHead (const IotListDouble_t* const pList) /* @[declare_linear_containers_list_double_peekhead] */ { IotLink_t* pHead = NULL; if (pList != NULL) { if (IotListDouble_IsEmpty(pList) == false) { pHead = pList->pNext; } } return pHead; } 

Em seguida, a condição pQItem == NULL é atendida e o fluxo de controle prossegue via goto para a parte inferior do corpo da função. A essa altura, o ponteiro pCurrentHttpsResponse permanecerá nulo e o status não será mais igual a IOT_HTTPS_OK . Como resultado, cairemos nesse mesmo ramo se , e ... amplos! As consequências dessa desreferenciação você mesmo conhece.

Ok Foi um exemplo levemente ornamentado. Agora, trago à sua atenção uma desreferenciação potencial muito simples e compreensível:

 int PKI_mbedTLSSignatureToPkcs11Signature (uint8_t * pxSignaturePKCS, uint8_t * pxMbedSignature ) { int xReturn = 0; uint8_t * pxNextLength; /* The 4th byte contains the length of the R component */ uint8_t ucSigComponentLength = pxMbedSignature[ 3 ]; // <= if( ( pxSignaturePKCS == NULL ) || ( pxMbedSignature == NULL ) ) { xReturn = FAILURE; } .... } 

Aviso do PVS-Studio: V595 [CWE-476] O ponteiro 'pxMbedSignature' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 52, 54. iot_pki_utils.c 52

Esta função obtém dois ponteiros para uint8_t . Os dois ponteiros são verificados quanto a NULL , o que é uma boa prática - essas situações devem ser resolvidas imediatamente.

Mas aqui está a má sorte: quando o pxMbedSignature for verificado, ele já será desreferenciado literalmente uma linha acima. Ta-daa!

Outro exemplo de código especulativo:

 CK_RV vAppendSHA256AlgorithmIdentifierSequence ( uint8_t * x32ByteHashedMessage, uint8_t * x51ByteHashOidBuffer ) { CK_RV xResult = CKR_OK; uint8_t xOidSequence[] = pkcs11STUFF_APPENDED_TO_RSA_SIG; if( ( x32ByteHashedMessage == NULL ) || ( x51ByteHashOidBuffer == NULL ) ) { xResult = CKR_ARGUMENTS_BAD; } memcpy( x51ByteHashOidBuffer, xOidSequence, sizeof( xOidSequence ) ); memcpy( &x51ByteHashOidBuffer[ sizeof( xOidSequence ) ], x32ByteHashedMessage, 32 ); return xResult; } 

Avisos do PVS-Studio:

  • V1004 [CWE-628] O ponteiro 'x51ByteHashOidBuffer' foi usado sem segurança após ser verificado com relação ao nullptr. Verifique as linhas: 275, 280. iot_pkcs11.c 280
  • V1004 [CWE-628] O ponteiro 'x32ByteHashedMessage' foi usado sem segurança após ser verificado no nullptr. Verifique as linhas: 275, 281. iot_pkcs11.c 281

O analisador avisa que os parâmetros de função que são indicadores são usados ​​de maneira insegura após serem testados quanto a NULL . De fato, os argumentos são verificados, mas se algum deles for NULL , nenhuma ação será tomada, exceto a gravação em xResult . Este trecho de código parece dizer: “Sim, isso significa que os argumentos acabaram sendo ruins. Vamos anotá-lo agora, enquanto você continuar, continue. "

Bottom line: NULL será passado para memcpy . O que pode vir disso? Onde os valores serão copiados e quais? De fato, adivinhar isso não vale a pena, porque a norma afirma claramente que essa chamada leva a um comportamento indefinido (consulte o parágrafo 1).

Figura 2


O relatório do analisador ainda contém exemplos de operação incorreta com ponteiros que encontrei no Amazon FreeRTOS, mas acho que os exemplos acima são suficientes para mostrar os recursos do PVS-Studio na detecção de tais erros. Considere algo novo.

VERDADEIRO! = 1


Vários erros que encontrei estavam relacionados a um padrão que, infelizmente, é frequentemente esquecido.

O fato é que o tipo bool (de C ++) é diferente do tipo BOOL (geralmente usado em C). O primeiro pode conter apenas verdadeiro ou falso . O segundo é um typedef de algum tipo inteiro ( int , long , etc.). Para ele, "falso" é o valor 0 e "verdadeiro" é qualquer valor diferente de zero.

Como não há tipo booleano interno em C, essas constantes são definidas por conveniência:

 #define FALSE 0 #define TRUE 1 

Agora considere um exemplo:

 int mbedtls_hardware_poll(void* data, unsigned char* output, size_t len, size_t* olen) { int lStatus = MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; HCRYPTPROV hProv = 0; /* Unferenced parameter. */ (void)data; /* * This is port-specific for the Windows simulator, * so just use Crypto API. */ if (TRUE == CryptAcquireContextA( &hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { if (TRUE == CryptGenRandom(hProv, len, output)) { lStatus = 0; *olen = len; } CryptReleaseContext(hProv, 0); } return lStatus; } 

Avisos do PVS-Studio:

  • V676 [CWE-253] É incorreto comparar a variável do tipo BOOL com TRUE. aws_entropy_hardware_poll.c 48
  • V676 [CWE-253] É incorreto comparar a variável do tipo BOOL com TRUE. A expressão correta é: 'FALSE! = CryptGenRandom (hProv, len, output)'. aws_entropy_hardware_poll.c 51

Encontrou um erro? E é :) Funções CryptAcquireContextA e CryptGenRandom são funções padrão do cabeçalho wincrypt.h . Se for bem-sucedido, eles retornam um valor diferente de zero. Eu enfatizo - diferente de zero . Portanto, teoricamente, esse valor pode ser diferente de zero: 1 , 314 , 42 , 420 .

Aparentemente, o programador que escreveu a função do exemplo não pensou nisso e, como resultado, os valores obtidos são comparados com a unidade.

Com que probabilidade a condição TRUE == CryptGenRandom (....) não é atendida? Difícil dizer. Talvez CryptGenRandom retorne uma unidade com mais frequência do que outros valores, e talvez sempre retorne apenas um. Não podemos ter certeza: a implementação desta função criptográfica está oculta aos olhos dos programadores mortais :)

É importante lembrar que essas comparações são potencialmente perigosas. E em vez de:

 if (TRUE == GetBOOL()) 

use uma opção mais segura:

 if (FALSE != GetBOOL()) 

Problemas de otimização


Vários avisos do analisador foram associados a construções de execução lenta. Por exemplo:

 int _IotHttpsDemo_GetS3ObjectFileSize(....) { .... pFileSizeStr = strstr(contentRangeValStr, "/"); .... } 

PVS-Studio Warning: V817 É mais eficiente procurar o caractere '/' do que uma string. iot_demo_https_common.c 205

Breve e claramente, não é? A função strstr aqui é usada para procurar apenas um caractere, que é passado ao parâmetro como uma string (entre aspas duplas).

Este local pode ser potencialmente otimizado substituindo strstr por strchr :

 int _IotHttpsDemo_GetS3ObjectFileSize(....) { .... pFileSizeStr = strchr(contentRangeValStr, '/'); .... } 

Em seguida, a pesquisa funcionará um pouco mais rápido. Um pouco, mas legal.

Essas otimizações são, obviamente, boas, e o analisador encontrou outro local que pode ser otimizado de maneira muito mais notável:

 void vRunOTAUpdateDemo(void) { .... for (; ; ) { .... xConnectInfo.cleanSession = true; xConnectInfo.clientIdentifierLength = (uint16_t)strlen(clientcredentialIOT_THING_NAME); xConnectInfo.pClientIdentifier = clientcredentialIOT_THING_NAME; .... } } 

Aviso PVS-Studio: V814 Desempenho reduzido. A função 'strlen' foi chamada várias vezes dentro do corpo de um loop. aws_iot_ota_update_demo.c 235

Hmmm ... Dentro do loop, a cada iteração, strlen é chamado, que cada vez calcula o comprimento da mesma linha. Não é a operação mais eficiente :)

Vamos dar uma olhada na definição de clientcredentialIOT_THING_NAME :

 /* * @brief Host name. * * @todo Set this to the unique name of your IoT Thing. */ #define clientcredentialIOT_THING_NAME "" 

O usuário é solicitado a digitar o nome do seu dispositivo aqui. Por padrão, está vazio e, neste caso, está tudo bem. Mas e se o usuário quiser inserir um nome longo e bonito lá? Por exemplo, eu adoraria chamar minha ideia de " Máquina de Café Apaixonada e Sofisticada BarBarista-N061E A Edição Final ". Você pode imaginar qual seria minha surpresa se, depois disso, minha linda máquina de café começar a funcionar um pouco mais devagar? Bagunça!

Para corrigir o erro, o strlen deve ser retirado do corpo do loop. Afinal, o nome do dispositivo não muda enquanto o programa está sendo executado. Ehhh, aqui seria constexpr de C ++ ...

Ok, ok, eu admito: aqui estava um pouco espessa. Como observou meu colega Andrei Karpov, os compiladores modernos sabem o que é strlen e ele observou pessoalmente como eles simplesmente usam uma constante no código binário se entenderem que o comprimento de uma string não pode mudar. Portanto, existe uma alta probabilidade de que, no modo de compilação da versão de lançamento, em vez do cálculo real do comprimento da string, um valor calculado anteriormente seja simplesmente usado. No entanto, isso nem sempre funciona, portanto, escrever esse código não é uma boa prática.

Algumas palavras sobre MISRA


O analisador PVS-Studio possui um grande conjunto de regras que permitem verificar seu código quanto à conformidade com os padrões MISRA C e MISRA C ++. Quais são esses padrões?

MISRA é o padrão de codificação para sistemas embarcados altamente responsivos. Ele contém um conjunto de regras e diretrizes estritas para escrever código e configurar o processo de desenvolvimento. Existem algumas dessas regras, e elas visam não apenas eliminar erros graves, mas também vários “odores de código”, além de escrever o código mais compreensível e legível.

Assim, seguir o padrão MISRA não apenas ajuda a evitar erros e vulnerabilidades, mas também significativamente - significativamente! - reduzir a probabilidade de ocorrência em um código existente.

O MISRA é usado nas indústrias aeroespacial, médica, automotiva e militar - onde as vidas humanas dependem da qualidade do software incorporado.

Aparentemente, os desenvolvedores do Amazon FreeRTOS estão cientes desse padrão e, em grande parte, o seguem. É isso mesmo: se você estiver escrevendo um sistema operacional de base ampla para sistemas embarcados, deve pensar em segurança.

No entanto, eu encontrei algumas violações do padrão MISRA. Não darei aqui exemplos de regras como "não use união" ou "uma função deve ter apenas um retorno no final do corpo" - infelizmente, elas não são espetaculares, como a maioria das regras MISRA. É melhor dar exemplos de violações que podem levar a consequências graves.

Vamos começar com as macros:

 #define FreeRTOS_ms_to_tick(ms) ( ( ms * configTICK_RATE_HZ + 500 ) / 1000 ) 

 #define SOCKETS_htonl( ulIn ) ( ( uint32_t ) \ ( ( ( ulIn & 0xFF ) << 24 ) | ( ( ulIn & 0xFF00 ) << 8 ) \ | ( ( ulIn & 0xFF0000 ) >> 8 ) | ( ( ulIn & 0xFF000000 ) >> 24 ) ) ) 

 #define LEFT_ROTATE( x, c ) ( ( x << c ) | ( x >> ( 32 - c ) ) ) 

Avisos do PVS-Studio:

  • V2546 [MISRA C 20.7] A macro e seus parâmetros devem estar entre parênteses. Considere inspecionar o parâmetro 'ms' da macro 'FreeRTOS_ms_to_tick'. FreeRTOS_IP.h 201
  • V2546 [MISRA C 20.7] A macro e seus parâmetros devem estar entre parênteses. Considere inspecionar o parâmetro 'ulIn' da macro 'SOCKETS_htonl'. iot_secure_sockets.h 512
  • V2546 [MISRA C 20.7] A macro e seus parâmetros devem estar entre parênteses. Considere inspecionar os parâmetros 'x', 'c' da macro 'LEFT_ROTATE'. iot_device_metrics.c 90

Sim, é exatamente isso que você pensou. Os parâmetros dessas macros não estão entre colchetes. Se alguém acidentalmente escreve algo como

 val = LEFT_ROTATE(A[i] | 1, B); 

a macro "chamada" será aberta em:

 val = ( ( A[i] | 1 << B ) | ( A[i] | 1 >> ( 32 - B ) ) ); 

Lembra das prioridades das operações? Primeiro, um deslocamento bit a bit é realizado e somente depois de um “ou” bit a bit. Portanto, a lógica do programa será violada. Um exemplo mais simples: o que acontece se a expressão " x + y " for passada para a macro FreeRTOS_ms_to_tick ? Um dos principais objetivos do MISRA é evitar a ocorrência de tais situações.

Alguém pode objetar: “se você tem programadores que não sabem disso, nenhum padrão o salvará!” E eu não vou concordar com isso. Os programadores também são pessoas e, por mais experiente que seja, ele também pode se cansar e cometer um erro no final do dia de trabalho. Esse é um dos motivos pelos quais a MISRA recomenda fortemente o uso de ferramentas de análise automatizada para validar um projeto quanto à conformidade com o padrão.

Dirijo-me aos desenvolvedores do Amazon FreeRTOS: o PVS-Studio encontrou mais 12 macros inseguras, para que você tenha mais cuidado com elas :)

Outra violação interessante de MISRA:

 /** * @brief Callback for an asynchronous request to notify * that the response is complete. * * @param[in] 0pPrivData - User private data configured * with the HTTPS Client library request configuration. * @param[in] respHandle - Identifier for the current response finished. * @param[in] rc - Return code from the HTTPS Client Library * signaling a possible error. * @param[in] status - The HTTP response status. */ static void _responseCompleteCallback(void* pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status) { bool* pUploadSuccess = (bool*)pPrivData; /* When the remote server response with 200 OK, the file was successfully uploaded. */ if (status == IOT_HTTPS_STATUS_OK) { *pUploadSuccess = true; } else { *pUploadSuccess = false; } /* Post to the semaphore that the upload is finished. */ IotSemaphore_Post(&(_uploadFinishedSem)); } 

Você pode encontrar o erro você mesmo?

PVS-Studio Warning: V2537 [MISRA C 2.7] As funções não devem ter parâmetros não utilizados. Considere inspecionar o parâmetro: 'rc'. iot_demo_https_s3_upload_async.c 234

Veja mais de perto: o parâmetro rc não é usado em nenhum lugar do corpo da função. Além disso, no comentário sobre a função, está explicitamente escrito que este parâmetro é um código de retorno de outra função e que pode sinalizar um erro. Então, por que esse parâmetro não é processado de forma alguma? Há claramente algo errado aqui.

No entanto, mesmo sem esses comentários, parâmetros não utilizados geralmente indicam lógica de programa quebrada. Caso contrário, por que eles são necessários na assinatura da função?

Aqui, dei uma pequena função que é adequada para um exemplo no artigo. Além dela, encontrei mais 10 parâmetros não utilizados. Muitos deles são usados ​​em funções maiores, e encontrá-los não é a coisa mais fácil.

É suspeito que eles não foram encontrados antes. De fato, os compiladores podem detectar facilmente esses casos.

Figura 1


Conclusão


Essas não eram todas as áreas problemáticas encontradas pelo analisador, mas o artigo já era bastante amplo. Espero que, graças a isso, os desenvolvedores do Amazon FreeRTOS possam corrigir algumas deficiências e talvez até desejem experimentar o PVS-Studio por conta própria. Dessa forma, será possível estudar os avisos mais detalhadamente e, de fato, trabalhar com uma interface conveniente é muito mais fácil do que examinar um relatório de texto.

Obrigado por ler nossos artigos! Vejo você na próxima edição: D

PS Aconteceu que este artigo foi publicado em 31 de outubro. Portanto, desejo a todos um feliz Dia das Bruxas!



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: George Gribkov. A pedido dos desenvolvedores incorporados: detectando erros no Amazon FreeRTOS .

Source: https://habr.com/ru/post/pt473972/


All Articles