PVS-Studio va a las nubes: GitLab CI / CD

Figura 2

Este artículo es una continuación de una serie de publicaciones sobre el uso de PVS-Studio en sistemas en la nube. Esta vez veremos el analizador junto con GitLab CI, un producto de GitLab Inc. La integración de un analizador estático en el sistema CI le permite identificar errores inmediatamente después de la fase de construcción del proyecto y es una forma muy efectiva de reducir el costo de detección de errores.

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 para gestionar repositorios. Se puede usar directamente en el navegador en el sitio web oficial registrando una cuenta, o instalado e implementado en su propio servidor.

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

El proyecto OBS se utilizará para demostrar el funcionamiento de un analizador estático en la nube. Open Broadcaster Software es un paquete de software gratuito y de código abierto para grabación y transmisión de video. OBS proporciona la capacidad de interceptar desde dispositivos y fuentes en tiempo real, composición de escenas, decodificación, grabación y transmisión. La transmisión de datos se lleva a cabo principalmente a través del Protocolo de mensajería en tiempo real, y los datos se pueden transferir a cualquier fuente que admita RTMP: el programa tiene preajustes listos para su transmisión directa a las plataformas de transmisión más populares.

Personalización


Para comenzar con GitLab, vaya al sitio y haga clic en el botón Registrarse :

Figura 6

Puede registrarse vinculando las cuentas de servicios como: GitHub, Twitter, Google, BitBucket, Saleforce o simplemente completando el formulario que se abre. Después de la autorización, GitLab nos recibe con una propuesta para crear un proyecto:

Figura 7

Lista de plataformas desde las que puede importar:

Figura 33


Para mayor claridad, cree un proyecto vacío:

Figura 17


A continuación, debemos cargar nuestro proyecto en el repositorio creado. Esto se realiza utilizando las indicaciones que aparecen en la ventana del proyecto creado.

Figura 1


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

Figura 21



Hagamos un contenedor mínimo para el script:

image: debian job: script: 

Descargue el analizador y la utilidad sendemail, que necesitaremos en el futuro:

 - 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, instale las 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 un archivo con la licencia del analizador. Por defecto, el archivo PVS-Studio.lic se creará en el directorio ../.config/PVS-Studio. En este caso, el archivo de licencia puede omitirse de los parámetros de inicio del analizador, se recogerá automáticamente:

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

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

Figura 23


Construya el proyecto usando cmake:

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

Luego ejecute el analizador:

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

PVS-Studio.log almacenará los resultados del análisis. El archivo de informe resultante no está destinado a leer, y para llevarlo a una vista accesible al ojo humano, necesitamos la utilidad plog-converter. Este programa convierte el registro del analizador a varios formatos. Para facilitar la lectura, convertiremos al formato html:

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

El informe se puede descargar usando artefactos , pero usaremos un método alternativo y enviaremos el archivo con los resultados del analizador al correo usando 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 el botón confirmar cambios . Si hicimos todo correctamente, veremos la inscripción: esta configuración de GitLab CI es válida. Para seguir el progreso, vaya a la pestaña CI / CD> Tuberías.

Figura 5


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

Figura 29


Entonces, es hora de ir al correo y abrir el archivo html con alertas.

Resultados de validación


Veamos ahora algunas advertencias del informe que indican errores en el proyecto Open Broadcaster Software para demostrar la esencia del análisis de código estático. El propósito del artículo es describir los principios de interacción entre PVS-Studio y GitLab CI / CD, por lo que solo se han escrito algunos fragmentos de código interesantes con errores. Estamos listos para otorgar una licencia temporal a los autores del proyecto y, si lo desean, pueden realizar un análisis más exhaustivo del proyecto. O pueden aprovechar una de las opciones de licencia gratuita de PVS-Studio.

Todos también pueden recibir de forma independiente una clave de prueba para explorar las capacidades de PVS-Studio y verificar sus proyectos.

Entonces, veamos 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); } .... } 

Preste atención a la línea: si (data_end_pos> cb-> capacidad) , el cumplimiento de la condición significará que la variable back_size , definida en la línea a continuación, siempre será mayor que cero, ya que lo obviamente más pequeño de lo obviamente más grande se resta, lo que significa que la condición es otra línea a continuación, siempre será cierta . Una condición en exceso no es tan inofensiva cuando se encuentra debajo de un código que modifica 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; .... } 

La combinación de operaciones en tipos de 32 bits y 64 bits parece sospechosa aquí. Primero, la máscara se calcula utilizando tipos de 32 bits (expresión (1 << sangría) - 1 ), y luego se expande implícitamente al tipo de 64 bits en la expresión activa & = .... Lo más probable es que, al calcular la máscara, también se supuso el uso de tipos de 64 bits.

La versión correcta del código:

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

O:

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

Además, a continuación se detalla copiar y pegar este bloque de código, sobre el cual el analizador también emitió una advertencia: 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 (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 siempre, dicho código indica un error de copiar y pegar . Lo más probable es que estas funciones deberían haberse llamado con diferentes argumentos. Incluso si no, este código se ve raro. Una buena solución sería escribir un bloque 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)) { .... } .... } 

La 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 que se inicializa con la dirección del campo de modificadores de la estructura combinada . Como su valor no cambia al lugar de verificación, permanecerá distinto de cero. Además, entre el lugar de inicialización y verificación, el puntero se usa cuando se llama a la función load_modifier , donde se desreferencia. En consecuencia, verificar los modificadores no tiene sentido, ya que como resultado del operador && , siempre obtendremos resultados falsos al evaluar una expresión lógica. Creo que el programador quería verificar el valor entero ubicado en la dirección almacenada en el puntero de modificadores , pero olvidó desreferenciar este puntero.

Es decir Me parece que el cheque debería ser así:

 if (!*modifiers && ....)  : 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); .... } 

A menudo, este código no es seguro porque no tiene en cuenta que malloc puede devolver un puntero nulo. Si malloc devuelve NULL , se producirá un comportamiento indefinido en este caso, ya que el primer argumento para strncpy será NULL .

Puede leer más sobre por qué es importante verificar el valor de retorno de la función malloc en el artículo correspondiente.

Advertencias N7, N8, N9

Tratemos de adivinar en cuál de los casos pueden ocurrir los 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)

La respuesta correcta: en aquellos en los que no estoy destinado a flotar . En las expresiones que nos muestra el analizador, se produce una división entera. Es posible que este código no funcione exactamente como esperaba el programador.

Conclusión


Como podemos ver, la integración del analizador estático PVS-Studio en su proyecto GitLab es bastante simple. Para hacer esto, solo escriba un archivo de configuración y póngalo 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 para configurar el sistema CI. Verificar el código le permitirá identificar problemas inmediatamente después de la compilación, lo que ayudará a solucionarlos cuando la complejidad y el costo de las ediciones aún sean pequeños.



Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Vladislav Stolyarov. PVS-Studio en las nubes: GitLab CI / CD .

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


All Articles