PVS-Studio nas nuvens: GitLab CI / CD

Figura 2

Este artigo continua a série de publicações sobre o uso do PVS-Studio em sistemas em nuvem. Desta vez, veremos como o analisador funciona junto com o GitLab CI, que é um produto fabricado pela GitLab Inc. A integração do analisador estático em um sistema de IC permite detectar erros logo após a criação do projeto e é uma maneira altamente eficaz de reduzir o custo de encontrar erros.

Uma lista de nossos outros artigos sobre a integração em sistemas de IC na nuvem:


Informações sobre o software usado


O GitLab é um serviço online projetado para gerenciar repositórios. Você pode usá-lo diretamente em um navegador no site oficial registrando sua conta ou instalá-lo e implantá-lo em seu próprio servidor.

O PVS-Studio é uma ferramenta projetada para detectar erros e possíveis vulnerabilidades no código fonte dos programas, escritos em C, C ++, C # e Java. Funciona em sistemas de 64 bits no Windows, Linux e macOS e pode analisar o código para plataformas ARM de 32 bits, 64 bits e incorporadas. Se for a primeira vez que você usa o analisador para verificar seus projetos, recomendamos que você leia o artigo sobre como verificar rapidamente os avisos mais interessantes do PVS-Studio e avaliar os recursos da ferramenta.

O projeto OBS será usado para demonstrar as habilidades do analisador estático na nuvem. Open Broadcaster Software é um conjunto de programas gratuito e aberto para gravação e streaming de vídeo. O OBS fornece interceptação de dispositivos e fontes em tempo real, composição de cenas, decodificação, gravação e transmissão. Os dados são transferidos principalmente através do Real Time Messaging Protocol e podem ser enviados para qualquer fonte que suporte RTMP - o programa possui pré-instalações prontas para transmissão ao vivo nas plataformas de streaming mais populares.

Configuração


Para começar a trabalhar com o GitLab, acesse o site e clique em Registrar :

Figura 6

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

Figura 7

Uma lista de plataformas disponíveis para importar arquivos:

Figura 33


Vamos criar um projeto vazio para maior clareza:

Figura 17


Em seguida, precisamos fazer o upload do nosso projeto no repositório criado. Faça isso usando as dicas que aparecem na janela do projeto criado.

Figura 1


Quando você inicia a tarefa, o GitLab CI recebe instruções do arquivo .gitlab-ci.yml . Você pode adicioná-lo clicando em Configurar IC / CD ou simplesmente criando um repositório local e fazendo o upload para o site. Vamos seguir a primeira opção:

Figura 21



Agora faça um wrapper mínimo para o script:

image: debian job: script: 

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

 - 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, instalaremos dependências e utilitários para a criação do 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 o arquivo com a licença do analisador (por padrão, o arquivo PVS-Studio.lic será criado no diretório ../.config/PVS-Studio). Ao fazer isso, você não precisa especificar o arquivo de licença nos parâmetros em execução do analisador, ele será capturado automaticamente):

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

Aqui PVS_NAME e PVS_KEY são os nomes de 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, siga: 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 

Depois disso, execute o analisador:

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

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

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

Você pode exportar o relatório usando artefatos , mas alteraremos a tacha e enviaremos o arquivo com os resultados do analisador por email, 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 em confirmar alterações . Se fizermos tudo certo, veremos a saída: Esta configuração de IC do GitLab é válida. Para acompanhar o progresso, vamos para a guia CI / CD> Pipelines .

Figura 5


Clique em execução . Veremos a janela do terminal da máquina virtual onde nosso arquivo de configuração é executado. Depois de um tempo, recebemos uma mensagem: o trabalho foi bem-sucedido.

Figura 29


Portanto, é hora de abrir o arquivo html com avisos enviados por email.

Resultados da análise


Vamos dar uma olhada em alguns avisos do relatório, revelando erros no projeto Open Broadcaster Software para obter a essência da análise de código estático. Como o principal objetivo do artigo é descrever os princípios da interação entre o PVS-Studio e o GitLab CI / CD, apenas vários exemplos não triviais foram escolhidos. Estamos prontos para conceder aos autores do projeto uma licença temporária e, se assim o desejarem, são bem-vindos para realizar uma análise mais completa do projeto. Além disso, eles podem usar uma das maneiras de obter uma licença gratuita do PVS-Studio .

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

Então, vamos prestar atenção a 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); } .... } 

A linha if (data_end_pos> cb-> capacidade) definitivamente vale a pena dar uma olhada. Se a condição for verdadeira, a variável back_size , definida na linha abaixo, sempre será maior que zero, pois aqui lidamos com a subtração do valor notoriamente menor do maior. No final, a condição, que é duas linhas abaixo, sempre será verdadeira . A condição redundante não é tão inofensiva quando é seguida pelo código, alterando 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; .... } 

As operações confusas nos tipos de 32 e 64 bits parecem suspeitas aqui. Primeiro, o programador avalia a máscara usando tipos de 32 bits (expressão (1 << recuo) - 1 ), depois expande implicitamente para o tipo de 64 bits na expressão ativa & = .... Provavelmente, ao avaliar a máscara, o uso de tipos de 64 bits também foi necessário.

Versão correta do código:

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

Ou:

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

A propósito, a versão copiar e colar deste bloco está abaixo, o analisador também emitiu o aviso para ele: 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 todos os casos, esse código indica um erro de copiar e colar . Muito provavelmente, essas funções devem ter sido chamadas com argumentos diferentes. Mesmo se não, esse código parece estranho. Uma boa solução seria escrever o bloco apenas 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)) { .... } .... } 

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 pelo endereço do campo modificadores da estrutura de combinação . Como seu valor não muda até a verificação, ele permanecerá não nulo. Além disso, após a inicialização antes da verificação, o ponteiro é usado ao chamar a função load_modifier , onde é desreferenciada. Portanto, a verificação ! Modifiers é inútil, pois, devido ao operador && , sempre seremos falsos ao avaliar a expressão lógica. Eu acho que o programador queria verificar um valor inteiro pelo endereço que está armazenado no ponteiro dos modificadores , mas esqueceu de desreferenciar esse ponteiro.

Portanto, parece-me que a verificação deve ser a seguinte:

 if (!*modifiers && ....) Or like this: 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); .... } 

Na maioria das vezes, esse código é inseguro, pois ignora que o malloc pode retornar um ponteiro nulo. Se malloc retornar NULL , um comportamento indefinido ocorrerá nesse caso, pois o primeiro argumento da função strncpy terá o valor NULL .

Para obter mais informações sobre por que é importante verificar o valor de retorno da função malloc , consulte o artigo relevante .

Avisos N7, N8, N9

Adivinhe quais casos contêm cálculos incorretos:

 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)

Aqui está a resposta certa: naqueles em que não sou obrigado a flutuar. O analisador nos mostra fragmentos com divisão inteira. Esse código pode não funcionar da maneira que o programador esperava.

Conclusão


Como podemos ver, a integração do analisador PVS-Studio em seu projeto no GitLab é um processo bastante simples. Para fazer isso, basta escrever apenas um arquivo de configuração e colocá-lo no seu repositório na nuvem. Devido ao fato de o GitLab ter sua própria máquina virtual integrada, não precisamos nem gastar muito tempo configurando o sistema de CI. A verificação de código permitirá encontrar problemas logo após a compilação. Isso ajuda a eliminar problemas no estágio em que sua complexidade e custo ainda são pequenos.

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


All Articles