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 :
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:
Lista de plataformas desde las que puede importar:
Para mayor claridad, cree un proyecto vacío:
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.
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:
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:
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.
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:
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.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.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 N1La 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, N3V629 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 N4V761 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 N5V560 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 N6V575 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, N9Tratemos 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:
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 .