O PVS-Studio vai para as nuvens: GitLab CI / CD

Figura 2

Este artigo é uma continuação de uma série de publicações sobre o uso do PVS-Studio em sistemas em nuvem. Desta vez, veremos o analisador em conjunto com o GitLab CI, um produto da GitLab Inc. A integração de um analisador estático no sistema de IC permite identificar erros imediatamente após a fase de construção do projeto e é uma maneira muito eficaz de reduzir o custo de detecção de erros.

Lista de outros artigos sobre integração em sistemas de IC na nuvem:


Informações sobre o software usado


GitLab é um serviço online para gerenciar repositórios. Ele pode ser usado diretamente no navegador no site oficial, registrando uma conta ou instalado e implantado em seu próprio servidor.

O PVS-Studio é uma ferramenta de análise de código estática projetada para detectar erros e possíveis vulnerabilidades em programas escritos em C, C ++, C # e Java. Ele funciona em sistemas de 64 bits no Windows, Linux e macOS e pode analisar o código destinado a plataformas ARM de 32 bits, 64 bits e incorporadas. Se for a primeira vez que você tenta a análise de código estático para testar seus projetos, recomendamos que você leia o artigo sobre como ver rapidamente os avisos mais interessantes do PVS-Studio e avaliar os recursos dessa ferramenta.

O projeto OBS será usado para demonstrar a operação de um analisador estático na nuvem. Open Broadcaster Software é um pacote de software gratuito e de código aberto para gravação e streaming de vídeo. O OBS oferece a capacidade de interceptar dispositivos e fontes em tempo real, composição de cenas, decodificação, gravação e transmissão. A transmissão de dados é realizada principalmente por meio do Real Time Messaging Protocol, e os dados podem ser transferidos para qualquer fonte que suporte RTMP - o programa possui predefinições prontas para transmissão direta para as plataformas de streaming mais populares.

Personalização


Para começar o GitLab, acesse o site e clique no botão Registrar :

Figura 6

Você pode se registrar vinculando as contas de serviços como: GitHub, Twitter, Google, BitBucket, Saleforce ou simplesmente preenchendo o formulário que é aberto. Após a autorização, o GitLab nos apresenta uma proposta para criar um projeto:

Figura 7

Lista de plataformas das quais você pode importar:

Figura 33


Para maior clareza, crie um projeto vazio:

Figura 17


Em seguida, precisamos enviar nosso projeto para o repositório criado. Isso é feito usando os prompts que aparecem na janela do projeto criado.

Figura 1


Quando a tarefa é iniciada, o GitLab CI recebe instruções do arquivo .gitlab-ci.yml . Você pode adicioná-lo clicando no botão Configurar CI / CD ou simplesmente criando-o no repositório local e enviando-o para o site. Usaremos a primeira opção:

Figura 21



Vamos criar um invólucro mínimo para o script:

image: debian job: script: 

Faça o download do analisador e do utilitário sendemail, que precisaremos no futuro:

 - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail 

Em seguida, instale as dependências e utilitários para criar o OBS:

 - apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates 

Agora precisamos criar um arquivo com a licença do analisador.Por padrão, o arquivo PVS-Studio.lic será criado no diretório ../.config/PVS-Studio. Nesse caso, o arquivo de licença pode ser omitido nos parâmetros de inicialização do analisador; ele será automaticamente captado:

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

Aqui PVS_NAME e PVS_KEY são os nomes das variáveis ​​cujos valores especificamos nas configurações. Eles irão armazenar o login e a chave de licença do PVS-Studio. Para definir seus valores, vá para: Configurações> CI / CD> Variáveis.

Figura 23


Crie o projeto usando o cmake:

 - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 

Em seguida, execute o analisador:

 - pvs-studio-analyzer analyze -o PVS-Studio.log 

O PVS-Studio.log armazenará os resultados da análise. O arquivo de relatório resultante não se destina à leitura e, para visualizá-lo acessível ao olho humano, precisamos do utilitário plog-converter. Este programa converte o log do analisador em vários formatos. Para facilitar a leitura, converteremos para o formato html:

 - plog-converter -t html PVS-Studio.log -o PVS-Studio.html 

O relatório pode ser baixado usando artefatos , mas usaremos um método alternativo e enviaremos o arquivo com os resultados do analisador para o correio usando o utilitário sendemail:

 - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

.Gitlab-ci.yml completo:

 image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail - apt-get -y install build-essential cmake pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev make libio-socket-ssl-perl libnet-ssleay-perl ca-certificates - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 - pvs-studio-analyzer analyze -o PVS-Studio.log - plog-converter -t html PVS-Studio.log -o PVS-Studio.html - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

Clique no botão confirmar alterações . Se fizermos tudo corretamente, veremos a inscrição: Esta configuração de IC do GitLab é válida. Para acompanhar o progresso, vá para a guia CI / CD> Pipelines.

Figura 5


Clique em execução . Veremos a janela do terminal da máquina virtual na qual nosso arquivo de configuração está sendo executado. Depois de algum tempo, recebemos a mensagem: trabalho bem-sucedido.

Figura 29


Portanto, é hora de ir para o correio e abrir o arquivo html com avisos.

Resultados da validação


Vejamos agora alguns avisos do relatório que indicam erros no projeto Open Broadcaster Software para demonstrar a essência da análise de código estático. O objetivo do artigo é descrever os princípios de interação entre o PVS-Studio e o GitLab CI / CD, para que apenas alguns fragmentos de código interessantes com erros tenham sido gravados. Estamos prontos para conceder uma licença temporária aos autores do projeto e, se assim o desejarem, eles poderão realizar uma análise mais aprofundada do projeto. Ou eles podem tirar proveito de uma das opções de licenciamento gratuito do PVS-Studio.

Todos também podem receber de forma independente uma chave de avaliação para explorar os recursos do PVS-Studio e verificar seus projetos.

Então, vejamos alguns exemplos de erros encontrados no Open Broadcaster Software.

Aviso N1

A expressão V547 'back_size' é sempre verdadeira. circlebuf.h (138)

 struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... } 

Preste atenção à linha: se (data_end_pos> cb-> capacidade) , o atendimento da condição significará que a variável back_size , definida na linha abaixo, sempre será maior que zero, pois a obviamente menor da obviamente maior é subtraída, o que significa que a condição é outra linha abaixo, sempre será verdadeira . Uma condição em excesso não é tão inofensiva quando está sob um código que modifica os dados.

Avisos N2, N3

V629 Considere inspecionar a expressão '1 << recuo'. Mudança de bit do valor de 32 bits com uma expansão subsequente para o tipo de 64 bits. profiler.c (610)

 static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... } 

A mistura de operações nos tipos de 32 e 64 bits parece suspeita aqui. Primeiro, a máscara é calculada usando tipos de 32 bits (expressão (1 << recuo) - 1 ) e depois é implicitamente expandida para o tipo de 64 bits na expressão active & = .... Provavelmente, ao calcular a máscara, o uso de tipos de 64 bits também foi assumido.

A versão correta do código:

 active &= ((uint64_t)(1) << indent) - 1; 

Ou:

 active &= (1ull << indent) - 1; 

Além disso, a cópia e colar deste bloco de código está abaixo, sobre a qual o analisador também emitiu um aviso sobre: V629 Considere inspecionar a expressão '1 << recuo'. Mudança de bit do valor de 32 bits com uma expansão subsequente para o tipo de 64 bits. profiler.c (719)

Aviso N4

V761 Foram encontrados quatro blocos de texto idênticos. 'obs-audio-controls.c' (353)

 static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... } 

Quatro blocos idênticos. Quase sempre, esse código indica um erro de copiar e colar . Muito provavelmente, essas funções deveriam ter sido chamadas com argumentos diferentes. Mesmo se não, esse código parece estranho. Uma boa solução seria escrever um bloco uma vez e envolvê-lo em um loop:

 for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); } 

Aviso N5

V560 Uma parte da expressão condicional é sempre falsa: '! Modifiers'. obs-hotkey.c (662)

 typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... } 

A definição da função load_modifier :

 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; } 

Como podemos ver, modificadores é um ponteiro inicializado com o endereço do campo modificadores da estrutura de combinação . Como seu valor não muda para o local da verificação, ele permanecerá diferente de zero. Além disso - entre o local de inicialização e verificação, o ponteiro é usado ao chamar a função load_modifier , onde é desreferenciada. Portanto, verificar ! Modificadores não faz sentido, pois como resultado do operador && , sempre seremos falsos ao avaliar uma expressão lógica. Eu acho que o programador quis verificar o valor inteiro localizado no endereço armazenado no ponteiro dos modificadores , mas esqueceu de desreferenciar esse ponteiro.

I.e. Parece-me que a verificação deve ser assim:

 if (!*modifiers && ....)  : if (!combo.modifiers && ....) 

Aviso N6

V575 O ponteiro nulo potencial é passado para a função 'strncpy'. Inspecione o primeiro argumento. Verifique as linhas: 2904, 2903. rtmp.c (2904)

 static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... } 

Freqüentemente, esse código é inseguro porque não leva em conta que o malloc pode retornar um ponteiro nulo. Se malloc retornar NULL , um comportamento indefinido ocorrerá nesse caso, pois o primeiro argumento para strncpy será NULL .

Você pode ler mais sobre por que é importante verificar o valor de retorno da função malloc no artigo correspondente.

Avisos N7, N8, N9

Vamos tentar adivinhar em qual dos casos os cálculos incorretos podem ocorrer:

 class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:// MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } } .... } 

Avisos do analisador:

  • V636 A expressão 'i / 6' foi implicitamente convertida do tipo 'size_t' para o tipo 'float'. Considere utilizar uma conversão de tipo explícita para evitar a perda de uma parte fracionária. Um exemplo: double A = (double) (X) / Y; window-projector.cpp (330)
  • V636 A expressão 'i / 2' foi convertida implicitamente do tipo 'size_t' para o tipo 'float'. Considere utilizar uma conversão de tipo explícita para evitar a perda de uma parte fracionária. Um exemplo: double A = (double) (X) / Y; window-projector.cpp (334)
  • V636 A expressão 'i / 2' foi convertida implicitamente do tipo 'size_t' para o tipo 'float'. Considere utilizar uma conversão de tipo explícita para evitar a perda de uma parte fracionária. Um exemplo: double A = (double) (X) / Y; window-projector.cpp (340)

A resposta correta: naqueles em que eu não sou lançado para flutuar . Nas expressões que o analisador nos mostra, ocorre a divisão inteira. Esse código pode não funcionar exatamente como o programador esperava.

Conclusão


Como podemos ver, a integração do analisador estático PVS-Studio ao seu projeto GitLab é bastante simples. Para fazer isso, basta escrever um arquivo de configuração e colocá-lo em seu repositório na nuvem. Devido ao fato de o GitLab ter sua própria máquina virtual integrada, nem precisamos gastar muito tempo para configurar o sistema de IC. A verificação do código permitirá identificar problemas imediatamente após a compilação, o que ajudará a corrigi-los quando a complexidade e o custo das edições ainda forem pequenos.



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Vladislav Stolyarov. PVS-Studio nas nuvens: GitLab CI / CD .

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


All Articles