Os perigos do uso de constantes com vários caracteres

Quadro 1

Durante a análise de código, o PVS-Studio analisa o fluxo de dados e opera com os valores das variáveis. Os valores são obtidos de constantes ou inferidos a partir de expressões condicionais. Nós os chamamos de valores virtuais. Recentemente, os aprimoramos para trabalhar com constantes com vários caracteres e esse foi o motivo da criação de uma nova regra de diagnóstico.

1. Introdução


Um literal de vários caracteres é definido pela implementação , portanto, diferentes compiladores podem codificar esses literais de maneiras diferentes. Por exemplo, GCC e Clang definem um valor com base na ordem dos caracteres em um literal, enquanto a MSVC os move dependendo do tipo de caractere (regular ou escape).

Por exemplo, o literal 'T \ x65s \ x74' será codificado de maneiras diferentes, dependendo do compilador. Uma lógica semelhante teve que ser adicionada ao analisador. Como resultado, criamos uma nova regra de diagnóstico V1039 para identificar esses literais no código. Tais literais são perigosos em projetos de plataforma cruzada que usam vários compiladores para montagem.

Diagnostics V1039


Considere um exemplo. O código abaixo, compilado por vários compiladores, se comportará de maneira diferente:

#include <stdio.h> void foo(int c) { if (c == 'T\x65s\x74') // <= V1039 { printf("Compiled with GCC or Clang.\n"); } else { printf("It's another compiler (for example, MSVC).\n"); } } int main(int argc, char** argv) { foo('Test'); return 0; } 

Um programa compilado por diferentes compiladores imprimirá diferentes mensagens na tela.

Para um projeto que usa um compilador específico, isso não será perceptível, mas a portabilidade pode causar problemas; portanto, você deve substituir esses literais por constantes numéricas simples, por exemplo, altere 'Teste' para 0x54657374.

Para demonstrar a diferença entre compiladores, escrevemos um pequeno utilitário em que seqüências de 3 e 4 caracteres são obtidas, por exemplo, 'GHIJ' e 'GHI', e sua representação na memória após a compilação ser exibida.

Código do Utilitário:

 #include <stdio.h> typedef int char_t; void PrintBytes(const char* format, char_t lit) { printf("%20s : ", format); const unsigned char *ptr = (const unsigned char*)&lit; for (int i = sizeof(lit); i--;) { printf("%c", *ptr++); } putchar('\n'); } int main(int argc, char** argv) { printf("Hex codes are: G(%02X) H(%02X) I(%02X) J(%02X)\n",'G','H','I','J'); PrintBytes("'GHIJ'", 'GHIJ'); PrintBytes("'\\x47\\x48\\x49\\x4A'", '\x47\x48\x49\x4A'); PrintBytes("'G\\x48\\x49\\x4A'", 'G\x48\x49\x4A'); PrintBytes("'GH\\x49\\x4A'", 'GH\x49\x4A'); PrintBytes("'G\\x48I\\x4A'", 'G\x48I\x4A'); PrintBytes("'GHI\\x4A'", 'GHI\x4A'); PrintBytes("'GHI'", 'GHI'); PrintBytes("'\\x47\\x48\\x49'", '\x47\x48\x49'); PrintBytes("'GH\\x49'", 'GH\x49'); PrintBytes("'\\x47H\\x49'", '\x47H\x49'); PrintBytes("'\\x47HI'", '\x47HI'); return 0; } 

A saída do utilitário compilado pelo Visual C ++:

 Hex codes are: G(47) H(48) I(49) J(4A) 'GHIJ' : JIHG '\x47\x48\x49\x4A' : GHIJ 'G\x48\x49\x4A' : HGIJ 'GH\x49\x4A' : JIHG 'G\x48I\x4A' : JIHG 'GHI\x4A' : JIHG 'GHI' : IHG '\x47\x48\x49' : GHI 'GH\x49' : IHG '\x47H\x49' : HGI '\x47HI' : IHG 

A saída de um utilitário compilado pelo GCC ou Clang:

 Hex codes are: G(47) H(48) I(49) J(4A) 'GHIJ' : JIHG '\x47\x48\x49\x4A' : JIHG 'G\x48\x49\x4A' : JIHG 'GH\x49\x4A' : JIHG 'G\x48I\x4A' : JIHG 'GHI\x4A' : JIHG 'GHI' : IHG '\x47\x48\x49' : IHG 'GH\x49' : IHG '\x47H\x49' : IHG '\x47HI' : IHG 

Conclusão


O diagnóstico V1039 foi adicionado ao analisador PVS-Studio versão 7.03 , lançado recentemente. Você pode baixar a versão mais recente do analisador na página de download .



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Svyatoslav Razmyslov. Os perigos do uso de constantes com vários caracteres

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


All Articles