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 :
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:
Uma lista de plataformas disponíveis para importar arquivos:
Vamos criar um projeto vazio para maior clareza:
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.
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:
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:
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.
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:
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 .
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.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 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); } .... }
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, 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; .... }
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 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 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 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)) { .... } .... }
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 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); .... }
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, N9Adivinhe 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:
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.