Por el momento, los sistemas de CI en la nube son un servicio muy demandado. En este artículo, le diremos cómo integrar el análisis del código fuente en una plataforma de nube CI con las herramientas que ya están disponibles en PVS-Studio. Como ejemplo, utilizaremos el servicio Travis CI.

¿Por qué consideramos nubes de terceros y no hacemos las nuestras? Hay varias razones, la principal es que la implementación de SaaS es un procedimiento bastante costoso y difícil. De hecho, es una tarea simple y trivial integrar directamente el análisis PVS-Studio en una plataforma de nube de terceros, ya sea plataformas abiertas como CircleCI, Travis CI, GitLab o una solución empresarial específica utilizada solo en una determinada empresa. Por lo tanto, podemos decir que PVS-Studio ya está disponible "en las nubes". Otro problema es implementar y garantizar el acceso a la infraestructura 24/7. Esta es una tarea más complicada. PVS-Studio no proporcionará su propia plataforma en la nube directamente para ejecutar análisis en ella.
Alguna 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 de programación para usar el servicio. Todos los ajustes se realizan en el archivo
.travis.yml ubicado en la raíz del repositorio.
Tomaremos
LXC (Contenedores de Linux) como un proyecto de prueba para PVS-Studio. Es un sistema de virtualización a nivel del sistema operativo para lanzar varias instancias del sistema operativo Linux en un nodo.
El proyecto es pequeño, pero más que suficiente para la demostración. 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.
Configuracion
Para comenzar a trabajar con Travis CI, seguimos el
enlace e iniciamos sesión con una cuenta GitHub.
En la ventana abierta necesitamos iniciar sesión en Travis CI.
Después de la autorización, redirige a la página de bienvenida "¿Primera vez aquí? ¡Vamos a empezar!
, donde encontramos una breve descripción de lo que se debe hacer después para comenzar:
- habilitar los repositorios;
- agregue el archivo .travis.yml en el repositorio;
- comienza la primera compilación.
Comencemos a hacer estas acciones.
Para agregar nuestro repositorio en Travis CI, vamos a la configuración del perfil mediante el
enlace y presionamos "Activar".
Una vez que se hace clic, se abrirá una ventana para seleccionar los repositorios a los que la aplicación Travis CI tendrá acceso.
Nota: para proporcionar acceso al repositorio, su cuenta debe tener derechos de administrador para hacerlo.
Después de eso, elegimos el repositorio correcto, confirmamos la opción con el botón "Aprobar e instalar", y se nos redirigirá a la página de configuración del perfil.
Agreguemos algunas variables que usaremos para crear el archivo de licencia del analizador y enviar sus informes. Para hacer esto, iremos a la página de configuración: el botón "Configuración" a la derecha del repositorio necesario.
Se abrirá la ventana de configuración.
Breve descripción de la configuración;
- Sección "General": configuración de activadores de tareas de inicio automático;
- La sección "Cancelación automática" permite configurar la cancelación automática de compilación;
- La sección "Variables de entorno" permite definir variables de entorno que contienen información abierta y confidencial, como información de inicio de sesión, teclas ssh;
- La sección "Trabajos de Cron" es una configuración del cronograma de ejecución de tareas.
En la sección "Variables de entorno" crearemos las variables
PVS_USERNAME y
PVS_KEY que contienen un nombre de usuario y una clave de licencia para el analizador estático respectivamente. Si no tiene una licencia permanente de PVS-Studio, puede
solicitar una licencia de prueba .
Aquí crearemos las variables
MAIL_USER y
MAIL_PASSWORD , que contienen un nombre de usuario y una contraseña de correo electrónico, que usaremos para enviar informes.
Al ejecutar tareas, Travis CI toma instrucciones del archivo .travis.yml, ubicado en la raíz del repositorio.
Al usar Travis CI, podemos ejecutar análisis estáticos directamente en la máquina virtual y utilizar un contenedor preconfigurado para hacerlo. Los resultados de estos enfoques no son diferentes entre sí. Sin embargo, 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 un producto de software y no queremos restaurar este entorno en Travis CI.
Creemos una configuración para ejecutar el analizador en una máquina virtual.
Para compilar y probar utilizaremos una máquina virtual en Ubuntu Trusty, su descripción está disponible en el
enlace .
En primer lugar, especificamos que el proyecto está escrito en C y enumeramos los compiladores que usaremos para la compilación:
language: c compiler: - gcc - clang
Nota: si especifica más de un compilador, las tareas se ejecutarán simultáneamente para cada uno de ellos. Lee más
aquí .
Antes de la compilación, necesitamos agregar el repositorio del analizador, establecer dependencias y paquetes adicionales:
before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https:
Antes de construir un proyecto, necesitamos preparar su 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 de licencia y comenzar a analizar el proyecto.
Luego creamos un archivo de licencia para el analizador con el primer comando. 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
Con el siguiente comando, comenzamos a rastrear la construcción del proyecto.
- pvs-studio-analyzer trace -- make -j4
Después de eso, ejecutamos análisis estáticos.
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
El archivo con los resultados del análisis se convierte en el informe html por el último comando.
- plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
Como TravisCI no le permite cambiar el formato de las notificaciones por correo electrónico, en el último paso usaremos el paquete sendemail para enviar informes:
- 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
Aquí está el texto completo del archivo de configuración para ejecutar el analizador en la 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 PVS-Studio en un contenedor, vamos a crearlo previamente 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 hacemos nada dentro de la máquina virtual, y todas las acciones para construir y probar el proyecto tienen lugar dentro del contenedor.
Nota : cuando inicia el contenedor, debe especificar el parámetro
--cap-add SYS_PTRACE o
--security-opt seccomp: inconfined , ya que se utiliza una llamada del sistema ptrace para el rastreo del compilador.
A continuación, cargamos el archivo de configuración en la raíz del repositorio y vemos que Travis CI ha sido notificado de los cambios en el proyecto y ha iniciado automáticamente la compilación.
Los detalles del progreso de la compilación y la verificación del analizador se pueden ver en la consola.
Una vez finalizadas las pruebas, recibiremos dos correos electrónicos: el primero, con resultados de análisis estáticos para construir un proyecto usando gcc, y el segundo, para clang, respectivamente.
Brevemente sobre los resultados de la verificación
En general, el proyecto es bastante limpio, el analizador emitió solo 24 advertencias de alta certeza y 46 de certeza media. Veamos 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). Comprobación redundante,
ret! = EOF se puede eliminar.
Se emitieron dos advertencias similares:
- 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. El uso de la
marca mo-> como máscara de bits provocará la pérdida de 32 bits altos. Una máscara de bits se convierte implícitamente en una variable entera de 64 bits después de la inversión bit a bit. Se pueden perder partes altas de esta máscara.
Lo mostraré usando un ejemplo:
unsigned long long x; unsigned y; .... x &= ~y;
Aquí está la versión correcta del código:
*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
Lazo 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 se inicia e interrumpe en la primera iteración. Esto podría haberse hecho intencionalmente, pero en este caso el bucle podría haberse omitido.
Índice de matriz fuera de límites
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 en el búfer de la tubería. En caso de error, la función
lxc_read_nointr devolverá un valor negativo. Si todo funciona correctamente, el último elemento escribe un terminal nulo. Sin embargo, si se leen 0 bytes, el índice estará fuera de los límites del búfer, lo que conducirá 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)
En este caso, el uso de
sscanf puede ser peligroso, porque si el búfer
oparq es más grande que el búfer de
nombre , el índice estará fuera de los límites al formar el búfer de
nombre .
Conclusión
Como vemos, es una tarea bastante simple configurar una verificación del analizador de código estático en una nube. Para esto, solo necesitamos agregar un archivo en un repositorio y pasar poco tiempo configurando el sistema CI. Como resultado, obtendremos una herramienta para detectar problemas en la etapa de escritura de código. La herramienta nos permite evitar que los errores pasen a las siguientes etapas de prueba, donde su reparación requerirá mucho tiempo y esfuerzos.
Por supuesto, el uso de PVS-Studio con plataformas en la nube no solo se limita a Travis CI. Similar al método descrito en el artículo, con pequeñas diferencias, el análisis PVS-Studio se puede integrar en otras soluciones populares de CI en la nube, como CircleCI, GitLab, etc.
Enlaces utiles
- Para obtener información adicional sobre cómo ejecutar PVS-Studio en Linux y MacOS, siga el enlace .
- También puede leer sobre la creación, configuración y uso de contenedores con el analizador de código estático PVS-Studio instalado mediante el enlace .
- TravisCI documentación .