No momento, os sistemas de IC na nuvem são um serviço altamente exigido. Neste artigo, mostraremos como integrar a análise do código-fonte em uma plataforma de nuvem de CI com as ferramentas que já estão disponíveis no PVS-Studio. Como exemplo, usaremos o serviço Travis CI.
Por que consideramos nuvens de terceiros e não criamos as nossas? Há várias razões, a principal delas é que a implementação do SaaS é um procedimento bastante caro e difícil. Na verdade, é uma tarefa simples e trivial integrar diretamente a análise do PVS-Studio em uma plataforma em nuvem de terceiros - sejam plataformas abertas como CircleCI, Travis CI, GitLab ou uma solução corporativa específica usada apenas em uma determinada empresa. Portanto, podemos dizer que o PVS-Studio já está disponível "nas nuvens". Outra questão é implementar e garantir o acesso à infraestrutura 24/7. Esta é uma tarefa mais complicada. O PVS-Studio não fornecerá sua própria plataforma de nuvem diretamente para executar análises nela.
Algumas 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 de programação para usar o serviço. Todas as configurações são feitas no arquivo
.travis.yml localizado na raiz do repositório.
Tomaremos o
LXC (Linux Containers) como um projeto de teste para o PVS-Studio. É um sistema de virtualização no nível do sistema operacional para ativar várias instâncias do sistema operacional Linux em um nó.
O projeto é pequeno, mas é mais do que suficiente para demonstração. 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 editaremos para nossos propósitos.
Configuração
Para começar a trabalhar com o Travis CI, seguimos o
link e efetue o login usando uma conta GitHub.
Na janela aberta, precisamos fazer login no Travis CI.
Após a autorização, ele é redirecionado para a página de boas-vindas "Primeira vez aqui? Vamos começar!
, onde encontramos uma breve descrição do que deve ser feito posteriormente para começar:
- habilitar os repositórios;
- adicione o arquivo .travis.yml no repositório;
- inicie a primeira compilação.
Vamos começar a fazer essas ações.
Para adicionar nosso repositório no Travis CI, acessamos as configurações do perfil no
link e pressione "Ativar".
Uma vez clicada, uma janela será aberta para selecionar repositórios aos quais o aplicativo Travis CI terá acesso.
Nota: para fornecer acesso ao repositório, sua conta deve ter direitos de administrador para isso.
Depois disso, escolhemos o repositório certo, confirme a opção com o botão "Aprovar e instalar" e seremos redirecionados de volta à página de configurações do perfil.
Vamos adicionar algumas variáveis que usaremos para criar o arquivo de licença do analisador e enviar seus relatórios. Para fazer isso, vamos para a página de configurações - o botão "Configurações" à direita do repositório necessário.
A janela de configurações será aberta.
Breve descrição das configurações;
- Seção "Geral" - configurando disparadores de tarefas de inicialização automática;
- A seção "Cancelamento automático" permite configurar o cancelamento automático da compilação;
- A seção “Variáveis de ambiente” permite definir variáveis de ambiente que contêm informações abertas e confidenciais, como informações de login, chaves ssh;
- A seção "Trabalhos Cron" é uma configuração do agendamento de tarefas em execução.
Na seção "Variáveis de ambiente", criaremos as variáveis
PVS_USERNAME e
PVS_KEY contendo um nome de usuário e uma chave de licença para o analisador estático, respectivamente. Se você não possui uma licença permanente do PVS-Studio, pode
solicitar uma licença de avaliação .
Aqui, criaremos as variáveis
MAIL_USER e
MAIL_PASSWORD , contendo um nome de usuário e uma senha de email, que usaremos para enviar relatórios.
Ao executar tarefas, o Travis CI recebe instruções do arquivo .travis.yml, localizado na raiz do repositório.
Usando o Travis CI, podemos executar a análise estática diretamente na máquina virtual e usar um contêiner pré-configurado para fazer isso. Os resultados dessas abordagens não são diferentes entre si. No entanto, o uso de um contêiner pré-configurado pode ser útil. Por exemplo, se já temos um contêiner com algum ambiente específico, dentro do qual um produto de software é construído e testado e não queremos restaurar esse ambiente no Travis CI.
Vamos criar uma configuração para executar o analisador em uma máquina virtual.
Para construir e testar, usaremos uma máquina virtual no Ubuntu Trusty, sua descrição está disponível no
link .
Primeiro, especificamos que o projeto seja escrito em C e listamos os compiladores que usaremos para a compilação:
language: c compiler: - gcc - clang
Nota: se você especificar mais de um compilador, as tarefas serão executadas simultaneamente para cada um deles. Leia mais
aqui .
Antes da compilação, precisamos adicionar o repositório do analisador, definir dependências e pacotes adicionais:
before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https:
Antes de criarmos um projeto, precisamos preparar seu 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 de licença e começar a analisar o projeto.
Em seguida, criamos um arquivo de licença para o analisador pelo primeiro comando. 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
Pelo próximo comando, começamos a rastrear a construção do projeto.
- pvs-studio-analyzer trace -- make -j4
Depois disso, executamos a análise estática.
Nota: ao usar uma licença de avaliação, você precisa especificar o parâmetro
--disableLicenseExpirationCheck .
- pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log --disableLicenseExpirationCheck
O arquivo com os resultados da análise é convertido no relatório html pelo último comando.
- 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, na última etapa, usaremos o pacote sendemail para enviar relatórios:
- 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
Aqui está o texto completo do arquivo de configuração para executar o analisador na 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 o PVS-Studio em um contêiner, vamos pré-criá-lo 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, nesse caso, não fazemos nada dentro da máquina virtual e todas as ações de criação e teste do projeto ocorrem dentro do contêiner.
Nota : ao iniciar o contêiner, é necessário especificar o parâmetro
--cap-add SYS_PTRACE ou
--security-opt seccomp: unconfined , pois uma chamada do sistema ptrace é usada para rastreamento do compilador.
Em seguida, carregamos o arquivo de configuração na raiz do repositório e vemos que o Travis CI foi notificado sobre alterações no projeto e iniciou automaticamente a compilação.
Detalhes do progresso da construção e verificação do analisador podem ser vistos no console.
Após a conclusão dos testes, receberemos dois emails: o primeiro - com resultados de análise estática para a construção de um projeto usando gcc, e o segundo - para clang, respectivamente.
Brevemente sobre os resultados da verificação
Em geral, o projeto é bastante limpo, o analisador emitiu apenas 24 avisos de alta certeza e 46 de média certeza. Vejamos 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). Verificação redundante,
ret! = EOF pode ser removido.
Dois avisos semelhantes 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. O uso do
sinalizador mo-> como uma máscara de bit levará à perda de 32 bits altos. Uma máscara de bits é convertida implicitamente em uma variável inteira de 64 bits após a inversão bit a bit. Bits altos dessa máscara podem ser perdidos.
Vou mostrar usando um exemplo:
unsigned long long x; unsigned y; .... x &= ~y;
Aqui está a versão correta do código:
*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
Loop 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 loop é iniciado e interrompido na primeira iteração. Isso pode ter sido feito intencionalmente, mas nesse caso o loop poderia ter sido omitido.
Índice de matriz fora dos limites
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 no buffer do canal. Em caso de erro, a função
lxc_read_nointr retornará um valor negativo. Se tudo der certo, um nulo terminal será gravado pelo último elemento. No entanto, se 0 bytes for lido, o índice ficará fora dos limites do buffer, levando 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)
Nesse caso, o uso do
sscanf pode ser perigoso, porque se o buffer
oparq for maior que o buffer de
nome , o índice ficará fora dos limites ao formar o buffer de
nome .
Conclusão
Como vemos, é uma tarefa bastante simples configurar uma verificação de analisador de código estático em uma nuvem. Para isso, basta adicionar um arquivo em um repositório e gastar pouco tempo configurando o sistema de IC. Como resultado, obteremos uma ferramenta para detectar problemas no estágio de escrita do código. A ferramenta nos permite impedir que os bugs cheguem aos próximos estágios dos testes, nos quais sua correção exigirá muito tempo e esforços.
Obviamente, o uso do PVS-Studio com plataformas em nuvem não se limita apenas ao Travis CI. Semelhante ao método descrito no artigo, com pequenas diferenças, a análise do PVS-Studio pode ser integrada a outras soluções populares de IC na nuvem, como CircleCI, GitLab, etc.
Links úteis
- Para informações adicionais sobre a execução do PVS-Studio no Linux e MacOS, siga o link .
- Você também pode ler sobre a criação, configuração e uso de contêineres com o analisador de código estático instalado do PVS-Studio pelo link .
- Documentação do TravisCI .