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.
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:
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.
Na janela que se abre, você precisa autorizar o Travis CI.
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.
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".
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.
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.
A janela de configurações será aberta.
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 .
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.
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:
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:
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:
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.
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)
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; for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) { *flags &= ~mo->flag;
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;
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) { if (sscanf(oparg, "%s", name) < 1)
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