Por el momento, los sistemas de CI en la nube son un servicio muy popular. En este artículo, le diremos cómo, utilizando las herramientas existentes disponibles en PVS-Studio, puede integrar el análisis de código fuente con la plataforma de CI en la nube, utilizando el servicio Travis CI como ejemplo.
¿Por qué miramos nubes de terceros y no hacemos las nuestras? Hay varias razones, y la principal es que SaaS es un procedimiento bastante costoso y difícil. De hecho, integrar directamente el análisis PVS-Studio con una plataforma en la nube de terceros (ya sea plataformas abiertas como CircleCI, Travis CI, GitLab o alguna solución empresarial especializada utilizada en una sola empresa específica) es una tarea bastante simple y trivial. Es decir, podemos decir que
PVS-Studio ya está disponible "en las nubes" . Un tema completamente diferente es la organización y provisión de infraestructura para dicho trabajo 24/7. Esta es una tarea completamente diferente, y PVS-Studio no tiene planes de proporcionar su propia plataforma en la nube directamente para ejecutar análisis en ella.
Información sobre el software utilizado
Travis CI es un servicio para crear y probar software que utiliza GitHub como almacenamiento. Travis CI no requiere cambiar el código del programa para usar el servicio, todas las configuraciones ocurren en el archivo
.travis.yml ubicado en la raíz del repositorio.
Tomaremos
LXC (Contenedores Linux) como un proyecto de prueba para probar con PVS-Studio. Es un sistema de virtualización a nivel de sistema operativo para ejecutar múltiples instancias del sistema operativo Linux en un solo nodo.
El proyecto es pequeño, pero más que suficiente para demostrarlo. La salida del comando cloc:
Nota: Los desarrolladores de LXC ya usan Travis CI, por lo que tomaremos su archivo de configuración como base y lo editaremos para nuestros propósitos.
Personalización
Para comenzar con Travis CI,
siga el enlace y autentíquese con una cuenta de GitHub.
En la ventana que se abre, debe autorizar Travis CI.
Después de la autorización, una redirección a la página de bienvenida “¿Primera vez aquí? ¡Vamos a empezar!
, que describe brevemente lo que se debe hacer a continuación para comenzar:
- activar repositorios;
- agregue el archivo .travis.yml al repositorio;
- ejecuta la primera compilación.
Comenzaremos a llevar a cabo estos puntos.
Para agregar nuestro repositorio a Travis CI, vaya a la configuración del perfil a
través del enlace y haga clic en el botón "Activar".
Después de hacer clic, se abre una ventana con una selección de repositorios a los que se otorgará acceso a la aplicación Travis CI.
Nota: para proporcionar acceso al repositorio, la cuenta debe tener derechos de administrador.
Seleccionamos el repositorio deseado, confirmamos la selección con el botón "Aprobar e instalar" y se nos redirigirá a la página de configuración del perfil.
Inmediatamente cree las variables que usaremos para crear el archivo de licencia del analizador y enviar sus informes. Para hacer esto, vaya a la página de configuración: el botón "Configuración" a la derecha del repositorio deseado.
Se abrirá la ventana de configuración.
Breve descripción de la configuración:
- Sección "General": configuración de disparadores para tareas de ejecución automática;
- Sección "Cancelación automática": le permite configurar el ensamblaje de cancelación automática;
- Sección "Variables de entorno": le permite definir variables de entorno que contienen información pública y confidencial, como credenciales, claves ssh;
- Sección "Cron Jobs": configuración del cronograma de inicio de tareas.
En la sección "Variables de entorno" creamos las variables
PVS_USERNAME y
PVS_KEY que contienen, respectivamente, el nombre de usuario y la clave de licencia para el analizador estático. Si no tiene una licencia permanente de PVS-Studio, puede
solicitar una licencia de prueba .
Inmediatamente cree las variables
MAIL_USER y
MAIL_PASSWORD que contienen el nombre de usuario y la contraseña del buzón, que usaremos para enviar informes.
Cuando se inicia la tarea, Travis CI toma instrucciones del archivo .travis.yml ubicado en la raíz del repositorio.
Usando Travis CI, podemos ejecutar análisis estáticos directamente en la máquina virtual, o usar un contenedor preconfigurado para esto. Los resultados de estos enfoques no son diferentes entre sí, pero el uso de un contenedor preconfigurado puede ser útil, por ejemplo, si ya tenemos un contenedor con un entorno específico dentro del cual se construye y prueba el producto de software, y no hay ningún deseo de restaurar este entorno en Travis CI .
Creemos una configuración para ejecutar el analizador en una máquina virtual.
Para el montaje y las pruebas, utilizaremos una máquina virtual basada en Ubuntu Trusty, su descripción se puede encontrar
aquí .
En primer lugar, indicamos que el proyecto está escrito en C y enumeramos los compiladores que usaremos para el ensamblaje:
language: c compiler: - gcc - clang
Nota : si especifica más de un compilador, las tareas se iniciarán en paralelo para cada uno de ellos. Lea más
en la documentación .
Antes de comenzar la compilación, necesitamos agregar el repositorio del analizador, instalar las dependencias y paquetes adicionales:
before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https:
Antes de construir el proyecto, debe preparar el entorno:
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
Luego, necesitamos crear un archivo con una licencia y ejecutar el análisis del proyecto.
El primer comando crea un archivo de licencia para el analizador. Los datos para las variables
$ PVS_USERNAME y
$ PVS_KEY se toman de la configuración del proyecto.
- pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
El siguiente comando inicia la traza del ensamblaje del proyecto:
- pvs-studio-analyzer trace -- make -j4
Después comenzamos el análisis estático.
Nota: cuando use una licencia de prueba, debe especificar el parámetro
--disableLicenseExpirationCheck .
- pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log –-disableLicenseExpirationCheck
Con el último comando, el archivo de resultados del analizador se convierte en un informe html.
- plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
Como TravisCI no permite cambiar el formato de las notificaciones por correo electrónico, utilizaremos el paquete sendemail para enviar informes en el último paso:
- 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 del archivo de configuración para ejecutar el analizador en una máquina virtual:
language: c compiler: - gcc - clang before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https:
Para ejecutar un analizador estático en un contenedor, primero créelo usando el siguiente 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:
En este caso, el archivo de configuración puede verse así:
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 puede ver, en este caso no estamos haciendo nada dentro de la máquina virtual, y absolutamente todas las acciones para ensamblar y probar el proyecto tienen lugar dentro del contenedor.
Nota : al iniciar el contenedor, debe especificar
--cap-add SYS_PTRACE o
--security-opt seccomp: parámetro no confinado , ya que la llamada al sistema ptrace se utiliza para compilar la traza.
Cargamos el archivo de configuración en la raíz del repositorio y vemos que Travis CI recibió una notificación sobre la presencia de cambios en el proyecto e inició automáticamente el ensamblaje.
Se puede ver información detallada sobre el progreso del ensamblaje y la verificación por parte del analizador en la consola.
Una vez que se completen las pruebas, recibiremos 2 cartas por correo: una con los resultados del análisis estático para construir el proyecto usando gcc y la segunda con clang, respectivamente.
Brevemente sobre los resultados de la prueba.
En general, el proyecto es bastante limpio, el analizador emitió solo 24 advertencias críticas y 46 advertencias promedio. Para demostrar el trabajo, considere un par de notificaciones interesantes:
Condiciones redundantes en if
V590 Considere inspeccionar la
expresión 'ret! = (- 1) && ret == 1'. La expresión es excesiva o contiene un error de imprenta. 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)
Si
ret == 1 , definitivamente no es igual a -1 (EOF). Validación excesiva,
ret! = EOF se puede eliminar.
Se emitieron dos advertencias más:
- V590 Considere inspeccionar la expresión 'ret! = (- 1) && ret == 1'. La expresión es excesiva o contiene un error de imprenta. attach.c 579
- V590 Considere inspeccionar la expresión 'ret! = (- 1) && ret == 1'. La expresión es excesiva o contiene un error de imprenta. attach.c 583
Pérdida de bits altos.
V784 El tamaño de la máscara de bits es menor que el tamaño del primer operando. Esto causará la pérdida de bits más 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;
En Linux,
long es una variable entera de 64 bits,
mo-> flag es una variable entera de 32 bits. Usar
mo-> flag como máscara de bits dará como resultado la pérdida de los 32 bits más significativos. Conversión implícita de la máscara de bits a una variable entera de 64 bits después de realizar la inversión bit a bit. Los bits altos de esta máscara serán cero.
Demuestre con un ejemplo:
unsigned long long x; unsigned y; .... x &= ~y;
El código correcto es:
*flags &= ~(unsigned long)(mo->flag);
El analizador emitió otra advertencia similar:
- V784 El tamaño de la máscara de bits es menor que el tamaño del primer operando. Esto causará la pérdida de bits más altos. conf.c 1933
Ciclo sospechoso
V612 Un 'retorno' incondicional dentro de un bucle. 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; }
El ciclo comienza y en la primera iteración se interrumpe. Tal vez esto fue intencionado, pero luego se puede omitir el ciclo.
Ir más allá de los límites de una matriz.
V557 Array underrun es posible. El valor del índice 'bytes - 1' podría alcanzar -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'; } .... }
Los bytes se leen desde la tubería al búfer. En caso de error, la función
lxc_read_nointr devolverá un valor negativo. Si todo salió bien, entonces la terminal nula se escribe como el último elemento. Sin embargo, si se leen 0 bytes, entonces el búfer se saldrá de los límites, lo que conduce a un comportamiento indefinido.
El analizador emitió otra advertencia similar:
- V557 Array underrun es posible. El valor del índice 'bytes - 1' podría alcanzar -1. network.c 2725
Desbordamiento de búfer
V576 Formato incorrecto. Considere verificar el tercer argumento real de la función 'sscanf'. Es peligroso usar un especificador de cadena sin especificación de ancho. El desbordamiento del búfer es posible. 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)
Usar
sscanf en este caso puede ser peligroso, porque si la longitud del búfer
oparq es mayor que la longitud del búfer de
nombre , se irá al extranjero cuando se forme el búfer de
nombre .
Conclusión
Como vimos, configurar una comprobación del analizador de código estático de nuestro proyecto en la nube es una tarea bastante simple. Para hacer esto, solo necesita agregar un archivo al repositorio y pasar el tiempo mínimo configurando el sistema CI. Como resultado, obtenemos una herramienta que le permite identificar código problemático en la etapa de escritura, y no permite que los errores pasen a las siguientes etapas de prueba, donde su corrección tomará más tiempo y recursos.
Por supuesto, el uso de PVS-Studio junto con plataformas en la nube no se limita a Travis CI. Por analogía con el método descrito en el artículo, con diferencias mínimas, el análisis PVS-Studio se puede integrar con otras soluciones de CI basadas en la nube populares, como CircleCI, GitLab, etc.
Enlaces utiles
- Puede encontrar información adicional sobre el lanzamiento de PVS-Studio en Linux y MacOS aquí .
- Puede leer sobre la creación, configuración y uso de contenedores con el analizador estático PVS-Studio instalado aquí .
- TravisCI Documentation .

Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Oleg Andreev.
PVS-Studio en las nubes: ejecución del análisis en Travis CI