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 :
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:
Lista de plataformas das quais você pode importar:
Para maior clareza, crie um projeto vazio:
Em seguida, precisamos enviar nosso projeto para o repositório criado. Isso é feito usando os prompts que aparecem na janela do projeto criado.
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:
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:
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.
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:
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.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.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 N1A 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, N3V629 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 N4V761 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 N5V560 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 N6V575 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, N9Vamos 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:
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 .