PVS-Studio en las nubes: GitLab CI / CD

Figura 2

Este artículo continúa la serie de publicaciones sobre el uso de PVS-Studio en sistemas en la nube. Esta vez veremos la forma en que funciona el analizador junto con GitLab CI, que es un producto fabricado por GitLab Inc. La integración del analizador estático en un sistema de CI permite detectar errores inmediatamente después de la construcción del proyecto y es una forma muy efectiva de reducir el costo de encontrar errores.

Una lista de nuestros otros artículos sobre integración en sistemas de CI en la nube:


Información sobre el software utilizado


GitLab es un servicio en línea diseñado para administrar repositorios. Puede usarlo directamente en un navegador en el sitio web oficial registrando su cuenta, o instalarlo e implementarlo en su propio servidor.

PVS-Studio es una herramienta diseñada para detectar errores y vulnerabilidades potenciales en el código fuente de los programas, escritos en C, C ++, C # y Java. Funciona en sistemas de 64 bits en Windows, Linux y macOS y puede analizar código para plataformas ARM integradas y de 32 bits. Si es la primera vez que usa el analizador para verificar sus proyectos, le recomendamos que lea el artículo sobre cómo revisar rápidamente las advertencias PVS-Studio más interesantes y evaluar las capacidades de la herramienta.

El proyecto OBS se utilizará para demostrar las habilidades del analizador estático en la nube. Open Broadcaster Software es un conjunto gratuito y abierto de programas para grabación y transmisión de video. OBS proporciona intercepción de dispositivos y fuentes en tiempo real, composición de escenas, decodificación, grabación y transmisión. Los datos se transfieren principalmente a través del Protocolo de mensajería en tiempo real y se pueden enviar a cualquier fuente que admita RTMP: el programa tiene preinstalaciones listas para su transmisión en vivo en las plataformas de transmisión más populares.

Configuracion


Para comenzar a trabajar con GitLab, vaya al sitio web y haga clic en Registrarse :

Figura 6

Puede registrarse vinculando cuentas de otros servicios como GitHub, Twitter, Google, BitBucket, Saleforce o simplemente completando el formulario abierto. Después de la autorización, GitLab nos invita a crear un proyecto:

Figura 7

Una lista de plataformas disponibles para importar archivos:

Figura 33


Creemos un proyecto vacío para mayor claridad:

Figura 17


Luego, necesitamos cargar nuestro proyecto en el repositorio creado. Hágalo usando las sugerencias que aparecen en la ventana del proyecto creado.

Figura 1


Cuando inicia la tarea, GitLab CI toma instrucciones del archivo .gitlab-ci.yml . Puede agregarlo haciendo clic en Configurar CI / CD , o simplemente creando un repositorio local y cargándolo en el sitio. Sigamos la primera opción:

Figura 21



Ahora haga un contenedor mínimo para el script:

image: debian job: script: 

Descargue la utilidad analizador y sendemail, que necesitaremos más adelante:

 - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail 

A continuación, instalaremos dependencias y utilidades para construir OBS:

 - apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates 

Ahora necesitamos crear el archivo con la licencia del analizador (por defecto, el archivo PVS-Studio.lic se creará en el directorio ../.config/PVS-Studio). Al hacerlo, no tiene que especificar el archivo de licencia en los parámetros de ejecución del analizador, se pondrá al día automáticamente):

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

Aquí PVS_NAME y PVS_KEY son los nombres de variables cuyos valores especificamos en la configuración. Almacenarán el nombre de usuario y la clave de licencia de PVS-Studio. Para configurar sus valores, siga: Configuración> CI / CD> Variables.

Figura 23


Construye el proyecto, usando cmake:

 - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 

Después de eso, ejecute el analizador:

 - pvs-studio-analyzer analyze -o PVS-Studio.log 

PVS-Studio.log almacenará los resultados del análisis. El archivo resultante con el informe no está destinado a la lectura. Para que sea accesible para un ojo humano, necesitamos la utilidad plog-converter. Este programa convierte el registro del analizador en diferentes formatos. Para facilitar la lectura, convierta el registro al formato html:

 - plog-converter -t html PVS-Studio.log -o PVS-Studio.html 

Puede exportar el informe utilizando artefactos , pero cambiaremos la táctica y enviaremos el archivo con los resultados del analizador por correo electrónico utilizando la utilidad sendemail:

 - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

Completo .gitlab-ci.yml:

 image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail - apt-get -y install build-essential cmake pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev make libio-socket-ssl-perl libnet-ssleay-perl ca-certificates - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 - pvs-studio-analyzer analyze -o PVS-Studio.log - plog-converter -t html PVS-Studio.log -o PVS-Studio.html - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

Haga clic en confirmar cambios . Si hicimos todo bien, veremos el resultado: esta configuración de GitLab CI es válida. Para seguir el progreso, pasemos a la pestaña CI / CD> Tuberías .

Figura 5


Haga clic en correr . Veremos la ventana del terminal de la máquina virtual donde se ejecuta nuestro archivo de configuración. Después de un tiempo recibimos un mensaje: el trabajo se realizó correctamente.

Figura 29


Así que es hora de abrir el archivo html con advertencias enviadas por correo.

Resultados de analisis


Echemos un vistazo a algunas advertencias del informe, que revelan errores en el proyecto Open Broadcaster Software para obtener la esencia del análisis de código estático. Dado que el objetivo principal del artículo es describir los principios de la interacción PVS-Studio y GitLab CI / CD, solo se han elegido varios ejemplos no triviales. Estamos listos para otorgar a los autores del proyecto una licencia temporal y, si lo desean, pueden realizar un análisis más exhaustivo del proyecto. Además, pueden usar una de las formas de obtener una licencia gratuita de PVS-Studio .

Además, todos pueden obtener una clave de prueba para explorar las capacidades de PVS-Studio y verificar sus proyectos.

Entonces, prestemos atención a algunos ejemplos de errores encontrados en Open Broadcaster Software.

Advertencia N1

La expresión V547 'back_size' siempre es verdadera. circlebuf.h (138)

 struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... } 

La línea if (data_end_pos> cb-> capacidad) definitivamente vale la pena echar un vistazo de cerca. Si la condición es verdadera, la variable back_size , definida en la línea a continuación, siempre será mayor que cero, ya que aquí tratamos con la resta del valor notoriamente menor del mayor. Al final, la condición, que está dos líneas debajo, siempre será verdadera . La condición redundante no es tan inofensiva cuando es seguida por el código, que cambia los datos.

Advertencias N2, N3

V629 Considere la posibilidad de inspeccionar la expresión '1 << sangría'. Desplazamiento de bits del valor de 32 bits con una expansión posterior al tipo de 64 bits. profiler.c (610)

 static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... } 

Las operaciones confusas sobre tipos de 32 bits y 64 bits parecen sospechosas aquí. Primero, el programador evalúa la máscara, usando tipos de 32 bits (expresión (1 << sangría) - 1 ), luego se expande implícitamente al tipo de 64 bits en la expresión activa & = ... Lo más probable es que, al evaluar la máscara, también se requiera el uso de tipos de 64 bits.

Versión correcta del código:

 active &= ((uint64_t)(1) << indent) - 1; 

O:

 active &= (1ull << indent) - 1; 

Por cierto, la versión de copiar y pegar de este bloque está debajo, el analizador también emitió la advertencia: V629 Considere inspeccionar la expresión '1 << sangría'. Desplazamiento de bits del valor de 32 bits con una expansión posterior al tipo de 64 bits. profiler.c (719)

Advertencia N4

V761 Se encontraron cuatro bloques de texto idénticos. 'obs-audio-controls.c' (353)

 static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... } 

Cuatro bloques idénticos. Casi en todos los casos, dicho código indica un error de copiar y pegar . Lo más probable es que estas funciones se hayan llamado con diferentes argumentos. Incluso si no, este código se ve extraño. Una buena solución sería escribir el bloque solo una vez y envolverlo en un bucle:

 for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); } 

Advertencia n5

V560 Una parte de la expresión condicional siempre es falsa: '! Modificadores'. obs-hotkey.c (662)

 typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... } 

Definición de la función load_modifier :

 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; } 

Como podemos ver, modificadores es un puntero, inicializado por la dirección del campo de modificadores de la estructura combinada . Dado que su valor no cambia hasta la comprobación, seguirá siendo no nulo. Además, después de la inicialización antes de la verificación, el puntero se usa cuando se llama a la función load_modifier , donde se desreferencia. En consecuencia, la verificación ! Modificadores no tiene sentido, ya que debido al operador && siempre obtendremos falso al evaluar la expresión lógica. Creo que el programador quería verificar un valor entero por la dirección que está almacenada en el puntero de modificadores , pero olvidó desreferenciar este puntero.

Entonces me parece que la verificación debería ser la siguiente:

 if (!*modifiers && ....) Or like this: if (!combo.modifiers && ....) 

Advertencia N6

V575 El puntero nulo potencial se pasa a la función 'strncpy'. Inspecciona el primer argumento. Líneas de verificación: 2904, 2903. rtmp.c (2904)

 static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... } 

Muy a menudo, dicho código no es seguro, ya que ignora que malloc puede devolver un puntero nulo. Si malloc devuelve NULL , se producirá un comportamiento indefinido en este caso, ya que el primer argumento de la función strncpy tendrá el valor NULL .

Para obtener más información sobre por qué es importante verificar el valor de retorno de la función malloc , consulte el artículo relevante .

Advertencias N7, N8, N9

Adivina qué casos contienen cálculos incorrectos:

 class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:// MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } } .... } 

Advertencias del analizador:

  • V636 La expresión 'i / 6' se convirtió implícitamente del tipo 'size_t' al tipo 'flotante'. Considere utilizar un molde de tipo explícito para evitar la pérdida de una parte fraccional. Un ejemplo: doble A = (doble) (X) / Y;. ventana-proyector.cpp (330)
  • V636 La expresión 'i / 2' se convirtió implícitamente del tipo 'size_t' al tipo 'flotante'. Considere utilizar un molde de tipo explícito para evitar la pérdida de una parte fraccional. Un ejemplo: doble A = (doble) (X) / Y;. ventana-proyector.cpp (334)
  • V636 La expresión 'i / 2' se convirtió implícitamente del tipo 'size_t' al tipo 'flotante'. Considere utilizar un molde de tipo explícito para evitar la pérdida de una parte fraccional. Un ejemplo: doble A = (doble) (X) / Y;. window -jector.cpp (340)

Aquí está la respuesta correcta: en las que no me lanzan a flotar. El analizador nos muestra fragmentos con división entera. Tal código podría no funcionar de la manera que el programador había esperado.

Conclusión


Como podemos ver, la integración del analizador PVS-Studio en su proyecto en GitLab es un proceso bastante simple. Para hacer esto, solo tiene que escribir un solo archivo de configuración y colocarlo en su repositorio en la nube. Debido al hecho de que GitLab tiene su propia máquina virtual integrada, ni siquiera necesitamos pasar mucho tiempo configurando el sistema CI. La comprobación de código le permitirá encontrar problemas justo después de la compilación. Esto ayuda a eliminar problemas en la etapa en que su complejidad y costo aún son pequeños.

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


All Articles