A pedido dos desenvolvedores incorporados: detectando erros no Amazon FreeRTOS

Qualquer pessoa que programa microcontroladores provavelmente conhece o FreeRTOS, ou pelo menos ouviu falar desse sistema operacional. Os desenvolvedores da Amazon decidiram aprimorar as habilidades desse 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 estático do PVS-Studio, fomos solicitados por correio e em comentários para verificar esses projetos. Bem, agora pegue o que você pediu. Continue lendo para descobrir o que saiu disso.



Brevemente sobre projetos


Para começar, vou falar um pouco sobre o precursor do projeto que está sendo testado - FreeRTOS (o código-fonte está disponível aqui por link ). Como afirma a Wikipedia, o FreeRTOS é um sistema operacional multitarefa em tempo real para sistemas embarcados.

Está escrito no bom e velho C, o que não é de surpreender - este sistema operacional deve funcionar em condições típicas de microcontroladores: baixo poder de processamento, uma pequena quantidade de RAM e similares. A linguagem C permite que você trabalhe com recursos em um nível baixo e tenha alto desempenho, portanto, é mais adequado para desenvolver um sistema operacional desse tipo.

Agora, de volta à Amazon, que está sempre em movimento, desenvolvendo várias direções promissoras. Por exemplo, a Amazon está desenvolvendo um mecanismo AAA Amazon Lumberyard, que também verificamos .

Uma dessas direções é a Internet das Coisas (IoT). Para se desenvolver nessa área, a Amazon decidiu criar seu próprio sistema operacional - e eles tomaram o núcleo do FreeRTOS como base.

O sistema resultante, o Amazon FreeRTOS, está posicionado para "fornecer uma conexão segura ao Amazon Web Services, como o AWS IoT Core ou o AWS IoT Greengrass". O código fonte deste projeto está disponível no Github.

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

O curso da verificação


A verificação foi realizada usando a ferramenta de busca automática de erros - o analisador de código estático PVS-Studio. É capaz de detectar erros em programas escritos em C, C ++, C # e Java.

Antes da análise, temos que construir o projeto. Dessa forma, estarei confiante de que tenho todas as dependências necessárias e que o projeto está pronto para ser verificado. Pode-se verificar o projeto de várias maneiras - por exemplo, usando um sistema de monitoramento de compilação. Nesse caso, eu realizei a análise usando o plug-in do Visual Studio - é bom que os repositórios de ambos os projetos contenham os conjuntos de arquivos de projeto que facilitam a compilação no Windows.

Eu apenas tive que criar projetos para ter tudo pronto para a verificação. Em seguida, executei a análise e - voila! - Eu tenho um relatório de analisador pronto na minha frente.

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

Assim, os projetos são analisados, os relatórios são recebidos, erros interessantes são destacados. É hora de obter a sua revisão!

O que o FreeRTOS oculta


Inicialmente, esperava escrever dois artigos separados: um para cada sistema operacional. Eu já estava esfregando as mãos? enquanto eu me preparava para escrever um bom artigo sobre o FreeRTOS. Antecipando a descoberta de pelo menos alguns erros interessantes (como o CWE-457 ), eu estava observando avisos esparsos do analisador e ... não encontrei nada. Não encontrei nenhum erro interessante.

Muitos dos avisos emitidos pelo analisador não eram relevantes para o FreeRTOS. Por exemplo, esses avisos eram falhas de 64 bits, como converter tamanho_t para uint32_t . Isso está relacionado ao fato de o FreeRTOS estar funcionando em dispositivos com um tamanho de ponteiro não superior a 32 bits.

Eu verifiquei minuciosamente todos os avisos do V1027 indicando vazamentos entre ponteiros para estruturas não relacionadas. Se as estruturas fundidas tiverem o mesmo alinhamento, esse vazamento será um erro. E eu não encontrei um único elenco perigoso!

Todos os outros lugares suspeitos estavam associados ao estilo de codificação ou receberam um comentário explicando por que foi feito dessa maneira e por que não foi um erro.

Então, eu gostaria de atrair os desenvolvedores do FreeRTOS. Gente, você é demais! Quase não vimos projetos tão limpos e de alta qualidade como o seu. E foi um prazer ler o código limpo, limpo e bem documentado. Tiremos o chapéu para vocês.

Mesmo não encontrando nenhum bug interessante naquele dia, eu sabia que não iria parar por aí. Eu estava voltando para casa com a firme confiança de que a versão da Amazon teria 100% de algo interessante e que amanhã eu definitivamente pegaria bugs suficientes para o artigo. Como você deve ter adivinhado, eu estava certa.

O que o Amazon FreeRTOS oculta


A versão do sistema da Amazon acabou sendo ... para dizer o mínimo, um pouco pior. O legado do FreeRTOS permaneceu tão limpo, enquanto as novas melhorias ocultaram muitas questões interessantes.

Alguns fragmentos tiveram a lógica do programa quebrada, alguns manipularam ponteiros incorretamente. Em alguns lugares, o código pode levar a um comportamento indefinido, e houve casos em que o programador simplesmente não sabia sobre o padrão de erro que cometeu. Eu até encontrei várias vulnerabilidades potenciais sérias.

Parece que eu reforcei a introdução. Vamos começar a descobrir erros!

Quebra da lógica do programa


Vamos começar com lugares problemáticos que obviamente indicam que o programa não funciona da maneira esperada pelo programador. O manuseio suspeito de matrizes virá primeiro:

/** * @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 é o ponteiro para o seu primeiro elemento. Ou seja, se _requestPool.pRequestDatas for uma matriz de estruturas, _requestPool.pRequestDatas [i] .scheduled é uma avaliação do membro agendado da estrutura da matriz i.E se escrevermos _requestPool.pRequestDatas-> agendadas , será possível que um membro da primeira estrutura de array será acessado.

No trecho do código acima, é o que acontece. Na última linha, o valor apenas do membro da primeira estrutura da matriz é alterado. Por si só, esse acesso já é suspeito, mas aqui o caso é ainda mais claro: a matriz _requestPool.pRequestDatas é avaliada por índice em todo o corpo da função. Mas 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 próximo erro está em uma função pequena, então eu darei isso completamente:

 /* 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; } 

Aviso do PVS-Studio: V642 [CWE-197] Salvar o resultado da função 'strncmp' dentro da variável do tipo 'curto' é inadequado. 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, o resultado do tipo int , com tamanho de 32 bits, é convertido em uma variável do tipo int16_t . Com essa conversão "restritiva", os bits mais antigos do valor retornado serão perdidos. Por exemplo, se a função strncmp retornar 0x00010000 , a unidade será perdida durante a conversão e a condição será executada.

É realmente estranho ver esse elenco na condição. Por que é necessário aqui, se um int comum pode ser comparado a zero? Por outro lado, se um programador desejava que essa função às vezes retornasse verdadeira, mesmo que não devesse, por que não apoiar um comportamento tão complicado com um comentário? Mas desta forma é algum tipo de backdoor. Enfim, estou inclinado a pensar que é um erro. O que você acha?

Comportamento indefinido e indicadores


Aqui vem um grande exemplo. Ele cobre uma desreferência potencial de 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 desreferenciamento de um ponteiro nulo em potencial 'pCurrentHttpsResponse'. iot_https_client.c 1184

O último bloco if contém desreferências problemáticas. Vamos descobrir o que está acontecendo aqui.

A função começa com as variáveis pCurrentHttpsResponse e pQItem inicializadas pelo valor NULL e a variável status é inicializada pelo valor IOT_HTTPS_OK , o que significa que está tudo correto.

A pQItem adicional é atribuído o valor, retornado da função IotDeQueue_PeekHead , que retorna o ponteiro para o início da fila com vínculo duplo.

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; } 

Além disso, a condição pQItem == NULL se tornará verdadeira e o fluxo de controle será passado por goto para a parte inferior da função. A essa altura, o ponteiro pCurrentHttpsResponse permanecerá nulo, enquanto o status não será igual a IOT_HTTPS_OK . No final, chegaremos ao mesmo se ramo, e ... boom! Bem, você conhece as conseqüências de tal desreferência.

Ok Foi um exemplo um pouco complicado. Agora, sugiro que você dê uma olhada em 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 recebe ponteiros para uint8_t . Ambos os ponteiros são verificados quanto a NULL , o que é uma boa prática - essas situações devem ser resolvidas imediatamente.

Mas aqui está o problema: 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 ponteiros são usados ​​com segurança após a verificação de NULL . De fato, os argumentos são verificados. Mas, se algum deles não for NULL , nenhuma ação será tomada, exceto a escrita em xResult. Esta seção do código diz: "Sim, então os argumentos acabaram sendo ruins. Vamos notar agora, e você - continue, continue.

Resultado: NULL será passado para o memcpy. O que pode resultar disso? Onde os valores serão copiados e quais? De fato, adivinhar não ajudará, pois o padrão afirma claramente que essa chamada leva a um comportamento indefinido (consulte a seção 1).

Figura 3


Existem outros exemplos de manipulação incorreta de ponteiros no relatório do analisador encontrado no Amazon FreeRTOS, mas acho que dados exemplos são suficientes para mostrar os recursos do PVS-Studio na detecção de tais erros. Vamos dar uma olhada em algo novo.

VERDADEIRO! = 1


Houve vários erros relacionados ao padrão, os quais, infelizmente, geralmente são ignorados.

O fato é que o tipo bool (de C ++) é diferente do tipo BOOL (comumente usado em C). O primeiro pode conter apenas um valor verdadeiro ou falso . O segundo é o typedef de um tipo inteiro ( int , long e outros). O valor 0 é "false" para ele e qualquer outro valor diferente de zero é "true".

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

 #define FALSE 0 #define TRUE 1 

Vejamos o 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

Você encontrou um erro? Não duvide, está aqui :) As funções CryptAcquireContextA e CryptGenRandom são funções padrão do cabeçalho wincrypt.h . Se for bem-sucedido, eles retornam o valor diferente de zero. Deixe-me enfatizar que é diferente de zero . Portanto, teoricamente, poderia haver qualquer valor diferente de zero: 1 , 314 , 42 , 420 .

Aparentemente, o programador que estava escrevendo a função a partir do exemplo não estava pensando nisso e, no final, os valores resultantes são comparados a um.

Qual a probabilidade de a condição TRUE == CryptGenRandom (....) não ser atendida? Difícil dizer. Talvez, CryptGenRandom possa retornar 1 com mais frequência do que outros valores, mas talvez retorne apenas 1. Não podemos ter certeza disso: a implementação dessa função criptográfica está oculta aos olhos dos programadores mortais :)

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

 if (TRUE == GetBOOL()) 

Use uma versão mais segura do código:

 if (FALSE != GetBOOL()) 

Problemas de otimização


Vários avisos do analisador estavam relacionados a estruturas de operação lenta. Por exemplo:

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

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

É curto e simples, não é? A função strstr é usada aqui para pesquisar apenas um caractere, passado no parâmetro como uma string (está entre aspas duplas).

Este local pode potencialmente ser otimizado substituindo strstr por strchr :

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

Dessa forma, a pesquisa funcionará um pouco mais rápido. Uma coisa pequena, mas agradável.

Bem, essas otimizações são boas, mas o analisador também encontrou outro lugar, que poderia ser otimizado de uma maneira muito mais perceptível:

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

Aviso do 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

Hmm ... Dentro do loop, a cada iteração, chama-se strlen que avalia o comprimento da mesma linha a cada vez. Não é a operação mais eficaz :)

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 inserir o nome do dispositivo aqui. Por padrão, está vazio e, neste caso, está tudo bem. E se um usuário quiser inserir um nome longo e bonito lá? Por exemplo, eu adoraria chamar minha ideia de " A máquina de café apaixonada e sofisticada BarBarista-N061E, The Ultimate Edition ". Você pode imaginar como seria minha surpresa se minha linda máquina de café começasse a trabalhar um pouco mais devagar depois disso? Incômodo!

Para corrigir o erro, vale a pena tomar strlen fora do loop do corpo. Afinal, o nome do dispositivo não muda durante o funcionamento do programa. Oh, constexpr de C ++ seria perfeitamente aqui ...

Ok, bem, não vamos dourar o lírio. Como observou meu colega Andrey Karpov, os compiladores modernos sabem o que é strlen e ele pessoalmente os assistia usando uma constante em código binário se entender que o comprimento da linha não pode mudar. Portanto, há uma boa chance de que, no modo de compilação do release, em vez de uma avaliação real do comprimento da linha, o valor pré-avaliado seja 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 para 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 responsáveis. Ele contém um conjunto de regras e diretrizes estritas para escrever código e configurar um processo de desenvolvimento. Essas regras são bastante e visam não apenas eliminar erros graves, mas também vários "odores de código". Também visa escrever o código mais compreensível e legível.

Portanto, seguir o padrão MISRA não apenas ajuda a evitar erros e vulnerabilidades, mas também reduz significativamente a probabilidade de aparecerem no código já 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 conhecem esse padrão e geralmente o seguem. Essa abordagem é absolutamente razoável: se você escreve um sistema operacional de base ampla para sistemas embarcados, deve pensar em segurança.

No entanto, eu encontrei muitas violações do padrão MISRA. Não vou dar exemplos de regras como "não use união" ou "função deve ter apenas um retorno no final do corpo" - infelizmente, elas não são espetaculares, como a maioria das regras MISRA. Prefiro dar exemplos de violações que podem levar a sérias conseqüências.

Vamos começar com 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ê está pensando. 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); 

essa "chamada" de uma macro será expandida para:

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

Lembra das prioridades das operações? Primeiro, é feita uma mudança bit a bit, e somente depois dela - um bit ou "bit". Portanto, a lógica do programa será quebrada. Um exemplo mais simples: o que aconteceria se a expressão " x + y " fosse passada na macro FreeRTOS_ms_to_tick ? Um dos principais objetivos do MISRA é evitar tais situações.

Alguns podem argumentar: "Se você tem programadores que não sabem disso, nenhum padrão pode ajudá-lo!" Eu não vou concordar com isso. Os programadores também são pessoas e, por mais experiente que seja, também podem se cansar e cometer um erro no final do dia. Essa é uma das razões pelas quais a MISRA recomenda enfaticamente o uso de ferramentas de análise automática para testar a conformidade de um projeto.

Deixe-me abordar os desenvolvedores do Amazon FreeRTOS: O PVS-Studio encontrou mais 12 macros inseguras, então você precisa ter 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 bug você mesmo?

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

Dê uma olhada: o parâmetro rc não é usado em nenhum lugar do corpo da função. Enquanto o comentário da função diz claramente que esse parâmetro é um código de retorno de outra função e que pode sinalizar um erro. Por que esse parâmetro não é tratado de forma alguma? Algo está claramente errado aqui.

No entanto, mesmo sem esses comentários, os parâmetros não utilizados frequentemente apontam para a lógica quebrada do programa. Caso contrário, por que você precisa deles na assinatura da função?

Aqui eu dei uma pequena função que é boa para um exemplo no artigo. Além disso, encontrei 10 outros parâmetros não utilizados. Muitos deles são usados ​​em funções maiores e não é fácil detectá-los.

Suspeita, eles não foram encontrados antes. Afinal, os compiladores detectam facilmente esses casos.

Figura 1


Conclusão


Esses não foram todos os problemas encontrados pelo analisador, mas o artigo já se mostrou bastante amplo. Espero que, graças a isso, os desenvolvedores do Amazon FreeRTOS sejam capazes de corrigir algumas das deficiências e talvez queiram experimentar o PVS-Studio por conta própria. Dessa forma, será mais conveniente investigar completamente os avisos. Na verdade, trabalhar com uma interface conveniente é muito mais fácil do que olhar para um relatório de texto.

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

PS Aconteceu que este artigo foi publicado em 31 de outubro. Feliz Dia das Bruxas, rapazes e meninas!

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


All Articles