PVS-Studio vai para as nuvens - análise de lançamento no Travis CI

No momento, os sistemas de IC na nuvem são um serviço muito popular. Neste artigo, mostraremos como, usando as ferramentas existentes disponíveis no PVS-Studio, você pode integrar a análise de código-fonte à plataforma de IC na nuvem, usando o serviço Travis CI como exemplo.

Quadro 1


Por que estamos vendo nuvens de terceiros e não criando nossas próprias? Existem várias razões, e a principal é que o SaaS é um procedimento bastante caro e difícil. De fato, integrar diretamente a análise do PVS-Studio a uma plataforma de nuvem de terceiros (sejam plataformas abertas como CircleCI, Travis CI, GitLab ou alguma solução corporativa especializada usada em apenas uma empresa específica) é uma tarefa bastante simples e trivial. Ou seja, podemos dizer que o PVS-Studio já está disponível "nas nuvens" . Uma questão completamente diferente é a organização e o fornecimento de infraestrutura para esse trabalho 24 horas por dia, 7 dias por semana. Essa é uma tarefa completamente diferente, e o PVS-Studio não tem planos de fornecer sua própria plataforma de nuvem diretamente para executar análises nela.

Informações sobre o software usado


O Travis CI é um serviço para criação e teste de software que usa o GitHub como armazenamento. O Travis CI não requer alteração do código do programa para usar o serviço, todas as configurações ocorrem no arquivo .travis.yml localizado na raiz do repositório.

Tomaremos o LXC (Linux Containers) como um projeto de teste para teste com o PVS-Studio. É um sistema de virtualização no nível do sistema operacional para executar várias instâncias do sistema operacional Linux em um único nó.

O projeto é pequeno, mas é mais do que suficiente para demonstrar. A saída do comando cloc:
Linguagem
arquivos
em branco
comentário
código
C
124
11937
6758
50836
Cabeçalho C / C ++
65
1117
3676
3774
Nota: Os desenvolvedores de LXC já usam o Travis CI, portanto, tomaremos o arquivo de configuração como base e o editaremos para nossos propósitos.

Personalização


Para iniciar o Travis CI, siga o link e autentique usando uma conta do GitHub.

Quadro 17

Na janela que se abre, você precisa autorizar o Travis CI.

Quadro 16

Após a autorização, um redirecionamento para a página de boas-vindas “Primeira vez aqui? Vamos começar! , que descreve brevemente o que precisa ser feito a seguir para começar:

  • ativar repositórios;
  • adicione o arquivo .travis.yml ao repositório;
  • execute a primeira compilação.

Quadro 18

Começaremos a realizar esses pontos.

Para adicionar nosso repositório ao Travis CI, acesse as configurações do perfil através do link e clique no botão "Ativar".

Quadro 19

Após clicar, uma janela é aberta com uma opção de repositórios aos quais o aplicativo Travis CI terá acesso.
Nota: para fornecer acesso ao repositório, a conta deve ter direitos de administrador.

Quadro 38

Selecionamos o repositório desejado, confirmamos a seleção com o botão "Aprovar e instalar" e seremos redirecionados de volta à página de configurações do perfil.

Crie imediatamente as variáveis ​​que usaremos para criar o arquivo de licença do analisador e enviar seus relatórios. Para fazer isso, vá para a página de configurações - o botão "Configurações" à direita do repositório desejado.

Quadro 39

A janela de configurações será aberta.

Quadro 41

Breve descrição das configurações:

  • Seção "Geral" - configurando gatilhos para tarefas de execução automática;
  • Seção "Cancelamento automático" - permite configurar o cancelamento automático de montagem;
  • Seção "Variáveis ​​de ambiente" - permite definir variáveis ​​de ambiente que contêm informações públicas e confidenciais, como credenciais, chaves ssh;
  • Seção “Trabalhos Cron” - definindo a programação de início da tarefa

Na seção "Variáveis ​​de ambiente", criamos as variáveis PVS_USERNAME e PVS_KEY que contêm, respectivamente, o nome de usuário e a chave de licença do analisador estático. Se você não tiver uma licença permanente do PVS-Studio, poderá solicitar uma licença de avaliação .

Quadro 5

Crie imediatamente as variáveis MAIL_USER e MAIL_PASSWORD contendo o nome de usuário e a senha da caixa de correio, que usaremos para enviar relatórios.

Quadro 4

Quando a tarefa é iniciada, o Travis CI recebe instruções do arquivo .travis.yml localizado na raiz do repositório.

Usando o Travis CI, podemos executar análises estáticas diretamente na máquina virtual ou usar um contêiner pré-configurado para isso. Os resultados dessas abordagens não são diferentes entre si, mas o uso de um contêiner pré-configurado pode ser útil, por exemplo, se já tivermos um contêiner com algum ambiente específico dentro do qual o produto de software é construído e testado, e não houver desejo de restaurar esse ambiente no Travis CI .

Vamos criar uma configuração para executar o analisador em uma máquina virtual.

Para montagem e teste, usaremos uma máquina virtual baseada no Ubuntu Trusty, sua descrição pode ser encontrada aqui .

Primeiro, indicamos que o projeto está escrito em C e listamos os compiladores que usaremos para montagem:

language: c compiler: - gcc - clang 

Nota : se você especificar mais de um compilador, as tarefas serão iniciadas em paralelo para cada um deles. Leia mais na documentação .

Antes de iniciar a construção, precisamos adicionar o repositório do analisador, instalar as dependências e pacotes adicionais:

 before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - - sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - sudo apt-get update -qq - sudo apt-get install -qq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates 

Antes de construir o projeto, você precisa preparar o ambiente:

 script: - ./coccinelle/run-coccinelle.sh -i - git diff --exit-code - export CFLAGS="-Wall -Werror" - export LDFLAGS="-pthread -lpthread" - ./autogen.sh - rm -Rf build - mkdir build - cd build - ../configure --enable-tests --with-distro=unknown 

Em seguida, precisamos criar um arquivo com uma licença e executar a análise do projeto.

O primeiro comando cria um arquivo de licença para o analisador. Os dados para as variáveis $ PVS_USERNAME e $ PVS_KEY são obtidos das configurações do projeto.

 - pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic 

O comando a seguir inicia o rastreio de montagem do projeto:

 - pvs-studio-analyzer trace -- make -j4 

Depois que começamos a análise estática.
Nota: ao usar uma licença de avaliação, você deve especificar o parâmetro --disableLicenseExpirationCheck .

  - pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log –-disableLicenseExpirationCheck 

Com o último comando, o arquivo de resultados do analisador é convertido em um relatório html.

 - plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html 

Como o TravisCI não permite alterar o formato das notificações por email, usaremos o pacote sendemail para enviar relatórios na última etapa:

 - sendemail -t mail@domain.com -u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -s smtp.gmail.com:587 -xu $MAIL_USER -xp $MAIL_PASSWORD -o tls=yes -f $MAIL_USER -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html 

Texto completo do arquivo de configuração para executar o analisador em uma máquina virtual:

 language: c compiler: - gcc - clang before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - - sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - sudo apt-get update -qq - sudo apt-get install -qq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates script: - ./coccinelle/run-coccinelle.sh -i - git diff --exit-code - export CFLAGS="-Wall -Werror" - export LDFLAGS="-pthread -lpthread" - ./autogen.sh - rm -Rf build - mkdir build - cd build - ../configure --enable-tests --with-distro=unknown - pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic - pvs-studio-analyzer trace -- make -j4 - pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log --disableLicenseExpirationCheck - plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html - sendemail -t mail@domain.com -u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -s smtp.gmail.com:587 -xu $MAIL_USER -xp $MAIL_PASSWORD -o tls=yes -f $MAIL_USER -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html 

Para executar um analisador estático em um contêiner, primeiro crie-o usando o seguinte Dockerfile:

 FROM docker.io/ubuntu:trusty ENV CFLAGS="-Wall -Werror" ENV LDFLAGS="-pthread -lpthread" RUN apt-get update && apt-get install -y software-properties-common wget \ && wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - \ && wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list \ && apt-get update \ && apt-get install -yqq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio git libtool autotools-dev automake pkg-config clang make libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates \ && rm -rf /var/lib/apt/lists/* 

Nesse caso, o arquivo de configuração pode ficar assim:

 before_install: - docker pull docker.io/oandreev/lxc env: - CC=gcc - CC=clang script: - docker run --rm --cap-add SYS_PTRACE -v $(pwd):/pvs -w /pvs docker.io/oandreev/lxc /bin/bash -c " ./coccinelle/run-coccinelle.sh -i && git diff --exit-code && ./autogen.sh && mkdir build && cd build && ../configure CC=$CC && pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic && pvs-studio-analyzer trace -- make -j4 && pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-$CC.log --disableLicenseExpirationCheck && plog-converter -t html -o PVS-Studio-$CC.html PVS-Studio-$CC.log && sendemail -t mail@domain.com -u 'PVS-Studio $CC report, commit:$TRAVIS_COMMIT' -m 'PVS-Studio $CC report, commit:$TRAVIS_COMMIT' -s smtp.gmail.com:587 -xu $MAIL_USER -xp $MAIL_PASSWORD -o tls=yes -f $MAIL_USER -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html" 

Como você pode ver, neste caso, não estamos fazendo nada dentro da máquina virtual, e absolutamente todas as ações para montar e testar o projeto ocorrem dentro do contêiner.

Nota : ao iniciar o contêiner, você deve especificar o parâmetro --cap-add SYS_PTRACE ou --security-opt seccomp: parâmetro não confinado , pois a chamada do sistema ptrace é usada para compilar o rastreio.

Carregamos o arquivo de configuração na raiz do repositório e vemos que o Travis CI recebeu uma notificação sobre a presença de alterações no projeto e iniciou automaticamente a montagem.

Informações detalhadas sobre o andamento da montagem e a verificação do analisador podem ser vistas no console.

Quadro 2

Após a conclusão dos testes, receberemos 2 cartas pelo correio: uma com os resultados da análise estática para a construção do projeto usando o gcc e a segunda com o clang, respectivamente.

Brevemente sobre os resultados do teste


Em geral, o projeto é bastante limpo, o analisador emitiu apenas 24 avisos críticos e 46 médios. Para demonstrar o trabalho, considere algumas notificações interessantes:

Condições redundantes em


V590 Considere inspecionar a expressão 'ret! = (- 1) && ret == 1'. A expressão é excessiva ou contém uma impressão incorreta. attach.c 107

 #define EOF -1 static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { .... while (getline(&line, &line_bufsz, proc_file) != -1) { ret = sscanf(line, "CapBnd: %llx", &info->capability_mask); if (ret != EOF && ret == 1) // <= { found = true; break; } } .... } 

Se ret == 1 , definitivamente não é igual a -1 (EOF). Validação excessiva, ret! = EOF pode ser removido.

Mais dois avisos foram emitidos:

  • V590 Considere inspecionar a expressão 'ret! = (- 1) && ret == 1'. A expressão é excessiva ou contém uma impressão incorreta. attach.c 579
  • V590 Considere inspecionar a expressão 'ret! = (- 1) && ret == 1'. A expressão é excessiva ou contém uma impressão incorreta. attach.c 583

Perda de bits altos


V784 O tamanho da máscara de bit é menor que o tamanho do primeiro operando. Isso causará a perda de bits mais altos. conf.c 1879

 struct mount_opt { char *name; int clear; int flag; }; static void parse_mntopt(char *opt, unsigned long *flags, char **data, size_t size) { struct mount_opt *mo; /* If opt is found in mount_opt, set or clear flags. * Otherwise append it to data. */ for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) { *flags &= ~mo->flag; // <= } else { *flags |= mo->flag; } return; } } .... } 

No Linux, long é uma variável inteira de 64 bits, o sinalizador mo-> é uma variável inteira de 32 bits. Usar o sinalizador mo-> como máscara de bit resultará na perda dos 32 bits mais significativos. A conversão implícita da máscara de bits em uma variável inteira de 64 bits após a inversão bit a bit é executada. Os bits altos dessa máscara serão zero.

Demonstre com um exemplo:

 unsigned long long x; unsigned y; .... x &= ~y; 

Quadro 3


O código correto é:

 *flags &= ~(unsigned long)(mo->flag); 

O analisador emitiu outro aviso semelhante:

  • V784 O tamanho da máscara de bit é menor que o tamanho do primeiro operando. Isso causará a perda de bits mais altos. conf.c 1933

Ciclo suspeito


V612 Um 'retorno' incondicional dentro de um loop. conf.c 3477

 #define lxc_list_for_each(__iterator, __list) \ for (__iterator = (__list)->next; __iterator != __list; \ __iterator = __iterator->next) static bool verify_start_hooks(struct lxc_conf *conf) { char path[PATH_MAX]; struct lxc_list *it; lxc_list_for_each (it, &conf->hooks[LXCHOOK_START]) { int ret; char *hookname = it->elem; ret = snprintf(path, PATH_MAX, "%s%s", conf->rootfs.path ? conf->rootfs.mount : "", hookname); if (ret < 0 || ret >= PATH_MAX) return false; ret = access(path, X_OK); if (ret < 0) { SYSERROR("Start hook \"%s\" not found in container", hookname); return false; } return true; // <= } return true; } 

O ciclo inicia e na primeira iteração é interrompido. Talvez isso tenha sido intencional, mas o ciclo pode ser omitido.

Indo além dos limites de uma matriz


V557 Array underrun é possível. O valor do índice 'bytes - 1' pode chegar a -1. network.c 2570

 static int lxc_create_network_unpriv_exec(const char *lxcpath, const char *lxcname, struct lxc_netdev *netdev, pid_t pid, unsigned int hooks_version) { int bytes; char buffer[PATH_MAX] = {0}; .... bytes = lxc_read_nointr(pipefd[0], &buffer, PATH_MAX); if (bytes < 0) { SYSERROR("Failed to read from pipe file descriptor"); close(pipefd[0]); } else { buffer[bytes - 1] = '\0'; } .... } 

Os bytes são lidos do tubo no buffer. Em caso de erro, a função lxc_read_nointr retornará um valor negativo. Se tudo correu bem, o terminal nulo é escrito como o último elemento. No entanto, se 0 bytes for lido, o buffer ficará fora dos limites, o que leva a um comportamento indefinido.

O analisador emitiu outro aviso semelhante:

  • V557 Array underrun é possível. O valor do índice 'bytes - 1' pode chegar a -1. network.c 2725

Estouro de buffer


V576 Formato incorreto. Considere verificar o terceiro argumento real da função 'sscanf'. É perigoso usar um especificador de string sem especificação de largura. O estouro de buffer é possível. lxc_unshare.c 205

 static bool lookup_user(const char *oparg, uid_t *uid) { char name[PATH_MAX]; .... if (sscanf(oparg, "%u", uid) < 1) { /* not a uid -- perhaps a username */ if (sscanf(oparg, "%s", name) < 1) // <= { free(buf); return false; } .... } .... } 

O uso do sscanf nesse caso pode ser perigoso, porque se o tamanho do buffer oparq for maior que o tamanho do buffer de nome , ele será transferido para o exterior quando o buffer de nome for formado.

Conclusão


Como vimos, a configuração de uma verificação de analisador de código estático do nosso projeto na nuvem é uma tarefa bastante simples. Para isso, basta adicionar um arquivo ao repositório e gastar o tempo mínimo configurando o sistema de IC. Como resultado, obtemos uma ferramenta que permite identificar código problemático no estágio de gravação e não permite que erros cheguem aos próximos estágios do teste, nos quais sua correção levará mais tempo e recursos.

Obviamente, o uso do PVS-Studio em conjunto com plataformas em nuvem não se limita ao Travis CI. Por analogia com o método descrito no artigo, com diferenças mínimas, a análise PVS-Studio pode ser integrada a outras soluções populares de CI baseadas em nuvem, como CircleCI, GitLab, etc.

Links úteis


  • Informações adicionais sobre o lançamento do PVS-Studio no Linux e MacOS podem ser encontradas aqui .
  • Você pode ler sobre como criar, configurar e usar contêineres com o analisador estático instalado do PVS-Studio aqui .
  • Documentação TravisCI .



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Oleg Andreev. PVS-Studio nas nuvens -Executando a análise no Travis CI

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


All Articles