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 valores variáveis. Os valores são obtidos de constantes ou derivados de expressões condicionais. Nós os chamamos de valores virtuais. Recentemente, nós os refinamos para trabalhar com constantes com vários caracteres e esse se tornou o motivo para criar uma nova regra de diagnóstico.

1. Introdução


Literais com vários caracteres são definidos pela implementação , portanto, diferentes compiladores podem codificá-los de maneiras diferentes. Por exemplo, GCC e Clang definem um valor, com base na ordem dos símbolos no literal, enquanto o MSVC os move dependendo do tipo de símbolo (regular ou escape).

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

Diagnostic V1039


Vejamos o exemplo. O código abaixo, compilado por diferentes 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; } 

O 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. Porém, ao portar, podem ocorrer problemas, portanto, é necessário substituir esses literais por constantes numéricas simples, como 'Teste', que deve ser alterado com 0x54657374.

Para demonstrar a diferença entre compiladores, escreveremos um pequeno utilitário que utiliza sequências de 3 e 4 símbolos, como 'GHIJ' e 'GHI', e exibe sua representação na memória após a compilação.

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

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 

Saída do 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 é adicionado ao analisador PVS-Studio da versão 7.03 , lançado recentemente. Você pode baixar a versão mais recente do analisador na página de download .

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


All Articles