C贸mo configurar PVS-Studio en Travis CI usando el emulador de consola de juegos PSP como ejemplo

PPSSPP

Travis CI es un servicio web distribuido para crear y probar software que utiliza GitHub como alojamiento de c贸digo fuente. Adem谩s de los escenarios anteriores, puede agregar el suyo propio, gracias a las amplias opciones de configuraci贸n. En este art铆culo, configuraremos Travis CI para que funcione con PVS-Studio usando el c贸digo PPSSPP de ejemplo.

Introduccion


Travis CI es un servicio web para crear y probar software. Por lo general, se usa junto con la pr谩ctica de la integraci贸n continua.

PPSSPP - Emulador de consola de juegos PSP. El programa puede emular el lanzamiento de cualquier juego desde im谩genes de disco dise帽adas para Sony PSP. El programa fue lanzado el 1 de noviembre de 2012. PPSSPP tiene licencia bajo la GPL v2. Cualquiera puede hacer mejoras al c贸digo fuente del proyecto .

PVS-Studio es un analizador de c贸digo est谩tico para buscar errores y vulnerabilidades potenciales en el c贸digo del programa. En este art铆culo, para variar, lanzaremos PVS-Studio no localmente en la m谩quina del desarrollador, sino en la nube, y buscaremos errores en PPSSPP.

Configurar Travis CI


Necesitaremos un repositorio en GitHub, donde se encuentra el proyecto que necesitamos, as铆 como una clave para PVS-Studio (puede obtener una clave de prueba o gratuita para proyectos de c贸digo abierto ).

Vayamos al sitio web de Travis CI . Despu茅s de la autorizaci贸n con la cuenta de GitHub, tendremos una lista de repositorios:


Para la prueba, bifurqu茅 PPSSPP.

Activamos el repositorio que queremos recopilar:


Por el momento, Travis CI no puede ensamblar nuestro proyecto, ya que no hay instrucciones para el ensamblaje. Por lo tanto, es hora de la configuraci贸n.

Durante el an谩lisis, algunas variables son 煤tiles para nosotros, por ejemplo, la clave para PVS-Studio, que ser铆a indeseable especificar en el archivo de configuraci贸n. Entonces agregue variables de entorno utilizando la configuraci贸n de compilaci贸n en Travis CI:


Necesitaremos:

  • PVS_USERNAME - nombre de usuario
  • PVS_KEY - clave
  • MAIL_USER: correo electr贸nico que se usar谩 para enviar el informe
  • MAIL_PASSWORD - contrase帽a de correo electr贸nico

Los dos 煤ltimos son opcionales. Se utilizar谩n para enviar los resultados por correo. Si desea enviar el informe de otra manera, no necesita especificarlos.

Entonces, agregamos las variables de entorno que necesitamos:


Ahora cree un archivo .travis.yml y p贸ngalo en la ra铆z del proyecto. El archivo de configuraci贸n para Travis CI ya exist铆a en PPSSPP, sin embargo, era demasiado grande y era completamente inapropiado para el ejemplo, as铆 que tuve que simplificarlo significativamente y dejar solo los elementos b谩sicos.

Primero, indicamos el idioma, la versi贸n de Ubuntu Linux que queremos usar en la m谩quina virtual y los paquetes necesarios para el ensamblaje:

language: cpp dist: xenial addons: apt: update: true packages: - ant - aria2 - build-essential - cmake - libgl1-mesa-dev - libglu1-mesa-dev - libsdl2-dev - pv - sendemail - software-properties-common sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:ubuntu-sdk-team/ppa' 

Todos los paquetes que se enumeran son solo para PPSSPP.

Ahora especifique la matriz de ensamblaje:

 matrix: include: - os: linux compiler: "gcc" env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes - os: linux compiler: "clang" env: PPSSPP_BUILD_TYPE=Linux 

Un poco m谩s sobre la secci贸n de la matriz . En Travis CI, hay dos formas de crear opciones de compilaci贸n: la primera es enumerar compiladores, tipos de sistemas operativos, variables de entorno, etc., despu茅s de lo cual se genera una matriz de todas las combinaciones posibles; el segundo es una indicaci贸n expl铆cita de la matriz. Por supuesto, puede combinar estos dos enfoques y agregar un caso 煤nico o, como alternativa, excluir utilizando la secci贸n de exclusi贸n . Lea m谩s sobre esto en la documentaci贸n de Travis CI .

Queda por especificar las instrucciones de montaje espec铆ficas del proyecto:

 before_install: - travis_retry bash .travis.sh travis_before_install install: - travis_retry bash .travis.sh travis_install script: - bash .travis.sh travis_script after_success: - bash .travis.sh travis_after_success 

Travis CI le permite agregar sus propios equipos para varias etapas de la vida de una m谩quina virtual. La secci贸n before_install se realiza antes de instalar paquetes. Luego instale , que sigue a la instalaci贸n de paquetes de la lista addons.apt , que indicamos anteriormente. El ensamblaje en s铆 tiene lugar en un script . Si todo sali贸 bien, entramos en after_success (es en esta secci贸n donde ejecutaremos el an谩lisis est谩tico). Estos no son todos los pasos que se pueden modificar; si necesita m谩s, debe buscar en la documentaci贸n de Travis CI .

Para facilitar la lectura, los comandos se movieron a un script separado .travis.sh , que se coloca en la ra铆z del proyecto.

Entonces tenemos el siguiente archivo .travis.yml :

 language: cpp dist: xenial addons: apt: update: true packages: - ant - aria2 - build-essential - cmake - libgl1-mesa-dev - libglu1-mesa-dev - libsdl2-dev - pv - sendemail - software-properties-common sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:ubuntu-sdk-team/ppa' matrix: include: - os: linux compiler: "gcc" env: PVS_ANALYZE=Yes - os: linux compiler: "clang" before_install: - travis_retry bash .travis.sh travis_before_install install: - travis_retry bash .travis.sh travis_install script: - bash .travis.sh travis_script after_success: - bash .travis.sh travis_after_success 

Antes de instalar los paquetes, actualice los subm贸dulos. Esto es necesario para construir PPSSPP. Agregue la primera funci贸n a .travis.sh (preste atenci贸n a la extensi贸n):

 travis_before_install() { git submodule update --init --recursive } 

Ahora hemos llegado directamente a configurar PVS-Studio para que se inicie autom谩ticamente en Travis CI. Primero, necesitamos instalar el paquete PVS-Studio en el sistema:

 travis_install() { if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8 fi if [ "$PVS_ANALYZE" = "Yes" ]; then wget -q -O - https://files.viva64.com/etc/pubkey.txt \ | sudo apt-key add - sudo wget -O /etc/apt/sources.list.d/viva64.list \ https://files.viva64.com/etc/viva64.list sudo apt-get update -qq sudo apt-get install -qq pvs-studio \ libio-socket-ssl-perl \ libnet-ssleay-perl fi download_extract \ "https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" \ cmake-3.6.2-Linux-x86_64.tar.gz } 

Al comienzo de la funci贸n travis_install, instalamos los compiladores que necesitamos usando variables de entorno. Luego, si la variable $ PVS_ANALYZE almacena el valor S铆 (lo especificamos en la secci贸n env durante la configuraci贸n de la matriz de ensamblaje), instalamos el paquete pvs-studio . Adem谩s, los paquetes libio-socket-ssl-perl y libnet-ssleay-perl tambi茅n est谩n indicados, sin embargo, son necesarios para enviar los resultados por correo, por lo que no son necesarios si elige un m茅todo diferente de entrega de informes.

La funci贸n download_extract descarga y desempaqueta el archivo especificado:

 download_extract() { aria2c -x 16 $1 -o $2 tar -xf $2 } 

Es hora de armar un proyecto. Esto sucede en la secci贸n del script :

 travis_script() { if [ -d cmake-3.6.2-Linux-x86_64 ]; then export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH fi CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}" if [ "$PVS_ANALYZE" = "Yes" ]; then CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}" fi cmake $CMAKE_ARGS CMakeLists.txt make } 

De hecho, esta es una configuraci贸n original simplificada, con la excepci贸n de estas l铆neas:

 if [ "$PVS_ANALYZE" = "Yes" ]; then CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}" fi 

En esta secci贸n de c贸digo, establecemos el indicador de exportaci贸n de los comandos de compilaci贸n para cmake . Esto es necesario para un analizador de c贸digo est谩tico. Puede leer m谩s sobre esto en el art铆culo " C贸mo ejecutar PVS-Studio en Linux y macOS ".

Si el ensamblaje fue exitoso, nos encontramos en after_success , donde realizamos un an谩lisis est谩tico:

 travis_after_success() { if [ "$PVS_ANALYZE" = "Yes" ]; then pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic \ -o PVS-Studio-${CC}.log \ --disableLicenseExpirationCheck plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html 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 fi } 

Consideremos las siguientes l铆neas con m谩s detalle:

 pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic \ -o PVS-Studio-${CC}.log \ --disableLicenseExpirationCheck plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html 

La primera l铆nea genera un archivo de licencia a partir del nombre de usuario y la clave que especificamos al principio al configurar las variables de entorno de Travis CI.

La segunda l铆nea comienza el an谩lisis directamente. El indicador -j <N> establece el n煤mero de secuencias para el an谩lisis, el indicador -l <archivo> indica la licencia, el indicador -o <archivo> define el archivo para la salida del registro y el indicador -disableLicenseExpirationCheck es necesario para las versiones de prueba, ya que por defecto pvs- studio-analyzer advertir谩 al usuario sobre el vencimiento de la licencia. Para evitar esto, puede especificar esta bandera.

El archivo de registro contiene una salida sin formato que no se puede leer sin conversi贸n, por lo que primero debe hacer que el archivo sea legible. Omitimos los registros a trav茅s del convertidor de plog , y la salida es un archivo html.

En este ejemplo, decid铆 enviar informes por correo usando el comando sendemail .

Como resultado, obtuvimos el siguiente archivo .travis.sh :

 #/bin/bash travis_before_install() { git submodule update --init --recursive } download_extract() { aria2c -x 16 $1 -o $2 tar -xf $2 } travis_install() { if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8 fi if [ "$PVS_ANALYZE" = "Yes" ]; then wget -q -O - https://files.viva64.com/etc/pubkey.txt \ | sudo apt-key add - sudo wget -O /etc/apt/sources.list.d/viva64.list \ https://files.viva64.com/etc/viva64.list sudo apt-get update -qq sudo apt-get install -qq pvs-studio \ libio-socket-ssl-perl \ libnet-ssleay-perl fi download_extract \ "https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" \ cmake-3.6.2-Linux-x86_64.tar.gz } travis_script() { if [ -d cmake-3.6.2-Linux-x86_64 ]; then export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH fi CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}" if [ "$PVS_ANALYZE" = "Yes" ]; then CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}" fi cmake $CMAKE_ARGS CMakeLists.txt make } travis_after_success() { if [ "$PVS_ANALYZE" = "Yes" ]; then pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic \ -o PVS-Studio-${CC}.log \ --disableLicenseExpirationCheck plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html 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 fi } set -e set -x $1; 

Es hora de agregar los cambios al repositorio de git, despu茅s de lo cual Travis CI iniciar谩 autom谩ticamente la compilaci贸n. Haga clic en "ppsspp" para ir a los informes de ensamblaje:


Veremos una descripci贸n general del ensamblaje actual:


En caso de completar con 茅xito el ensamblaje, recibiremos una carta por correo con los resultados del an谩lisis est谩tico. Por supuesto, el env铆o de correos no es la 煤nica forma de obtener un informe. Puede elegir cualquier m茅todo de implementaci贸n. Pero es importante recordar que despu茅s de que se complete el ensamblaje, ser谩 imposible acceder a los archivos de la m谩quina virtual.

Resumen de errores


Hemos completado con 茅xito la parte m谩s dif铆cil. Ahora asegur茅monos de que todos nuestros esfuerzos est茅n justificados. Considere algunos puntos interesantes del informe sobre an谩lisis est谩tico, que me lleg贸 por correo (no por nada que lo se帽al茅).

Optimizaci贸n peligrosa


 void sha1( unsigned char *input, int ilen, unsigned char output[20] ) { sha1_context ctx; sha1_starts( &ctx ); sha1_update( &ctx, input, ilen ); sha1_finish( &ctx, output ); memset( &ctx, 0, sizeof( sha1_context ) ); } 

Advertencia de PVS-Studio: V597 El compilador podr铆a eliminar la llamada a la funci贸n 'memset', que se utiliza para vaciar el b煤fer 'sum'. La funci贸n RtlSecureZeroMemory () debe usarse para borrar los datos privados. sha1.cpp 325

Este fragmento de c贸digo se encuentra en el m贸dulo de hashing seguro, sin embargo, contiene una falla de seguridad grave ( CWE-14 ). Considere la lista de ensambladores que se genera al compilar la versi贸n de depuraci贸n:

 ; Line 355 mov r8d, 20 xor edx, edx lea rcx, QWORD PTR sum$[rsp] call memset ; Line 356 

Todo est谩 en perfecto orden, y la funci贸n memset se ejecuta, sobrescribiendo as铆 datos importantes en RAM, sin embargo, no se regocije hasta ahora. Considere la lista de ensambladores de la versi贸n Release con optimizaci贸n:

 ; 354 : ; 355 : memset( sum, 0, sizeof( sum ) ); ; 356 :} 

Como se puede ver en la lista, el compilador ignor贸 la llamada de memset . Esto se debe a que la funci贸n sha1 ya no llama a la estructura ctx despu茅s de llamar a memset . Por lo tanto, el compilador no ve el punto de perder el tiempo del procesador sobrescribiendo la memoria no utilizada en el futuro. Puede solucionar esto utilizando la funci贸n RtlSecureZeroMemory o similar .

Correctamente:

 void sha1( unsigned char *input, int ilen, unsigned char output[20] ) { sha1_context ctx; sha1_starts( &ctx ); sha1_update( &ctx, input, ilen ); sha1_finish( &ctx, output ); RtlSecureZeroMemory(&ctx, sizeof( sha1_context ) ); } 

Comparaci贸n innecesaria


 static u32 sceAudioOutputPannedBlocking (u32 chan, int leftvol, int rightvol, u32 samplePtr) { int result = 0; // For some reason, this is the only one that checks for negative. if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) { .... } else { if (leftvol >= 0) { chans[chan].leftVolume = leftvol; } if (rightvol >= 0) { chans[chan].rightVolume = rightvol; } chans[chan].sampleAddress = samplePtr; result = __AudioEnqueue(chans[chan], chan, true); } } 

Advertencia de PVS-Studio: V547 La expresi贸n 'leftvol> = 0' siempre es verdadera. sceAudio.cpp 120

Presta atenci贸n a la rama else para el primer if . El c贸digo se ejecutar谩 solo si todas las condiciones son leftvol> 0xFFFF || rightvol> 0xFFFF || leftvol <0 || rightvol <0 resultar谩 ser falso. Por lo tanto, obtenemos las siguientes afirmaciones que ser谩n verdaderas para la rama else: leftvol <= 0xFFFF , rightvol <= 0xFFFF , leftvol> = 0 y rightvol> = 0 . Presta atenci贸n a las dos 煤ltimas declaraciones. 驴Tiene sentido verificar qu茅 es un requisito previo para ejecutar este fragmento de c贸digo?

Entonces podemos eliminar con calma estas declaraciones condicionales:

 static u32 sceAudioOutputPannedBlocking (u32 chan, int leftvol, int rightvol, u32 samplePtr) { int result = 0; // For some reason, this is the only one that checks for negative. if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) { .... } else { chans[chan].leftVolume = leftvol; chans[chan].rightVolume = rightvol; chans[chan].sampleAddress = samplePtr; result = __AudioEnqueue(chans[chan], chan, true); } } 

Otro escenario Detr谩s de estas condiciones redundantes hay alg煤n tipo de error. Quiz谩s no verificaron lo que se requiere.

Ctrl + C Ctrl + V Contraataca


 static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { if (!Memory::IsValidAddress(psmfData) || !Memory::IsValidAddress(psmfData)) { return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address"); } .... } 

V501 Hay subexpresiones id茅nticas '! Memory :: IsValidAddress (psmfData)' a la izquierda y a la derecha de '||' operador scePsmf.cpp 703

Presta atenci贸n al cheque dentro si . 驴No te parece extra帽o que verifiquemos si la direcci贸n psmfData es v谩lida el doble ? Entonces me parece extra帽o ... De hecho, tenemos, por supuesto, un error tipogr谩fico, y la idea era verificar ambos par谩metros de entrada.

La opci贸n correcta:

 static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { if (!Memory::IsValidAddress(psmfStruct) || !Memory::IsValidAddress(psmfData)) { return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address"); } .... } 

Variable olvidada


 extern void ud_translate_att( int size = 0; .... if (size == 8) { ud_asmprintf(u, "b"); } else if (size == 16) { ud_asmprintf(u, "w"); } else if (size == 64) { ud_asmprintf(u, "q"); } .... } 

Advertencia de PVS-Studio: V547 La expresi贸n 'size == 8' siempre es falsa. syn-att.c 195

Este error se encuentra en la carpeta ext , por lo que no se aplica al proyecto, pero el error se encontr贸 antes de notarlo, as铆 que decid铆 dejarlo. A煤n as铆, este art铆culo no trata sobre una revisi贸n de errores, sino sobre la integraci贸n con Travis CI, y no se realiz贸 ninguna configuraci贸n del analizador.

El tama帽o variable se inicializa mediante una constante, sin embargo, no se usa en absoluto en el c贸digo, hasta la instrucci贸n if , que, por supuesto, devuelve falso cuando se verifica la condici贸n, porque, como recordamos, el tama帽o es cero. Las comprobaciones posteriores tampoco tienen sentido.

Aparentemente, el autor del fragmento de c贸digo olvid贸 sobreescribir el tama帽o variable antes de esto.

Para


En esto, quiz谩s, terminemos con errores. El prop贸sito de este art铆culo es demostrar el trabajo de PVS-Studio en conjunto con Travis CI, y no analizar el proyecto tan a fondo como sea posible. Si quieres m谩s y m谩s errores hermosos, entonces siempre puedes admirarlos aqu铆 :).

Conclusi贸n


El uso de servicios web para crear proyectos junto con la pr谩ctica del an谩lisis incremental puede detectar muchos problemas inmediatamente despu茅s de la fusi贸n del c贸digo. Es cierto que un solo ensamblaje puede no ser suficiente, por lo que configurar las pruebas junto con el an谩lisis est谩tico mejorar谩 significativamente la calidad del c贸digo.

Enlaces utiles





Si desea compartir este art铆culo con una audiencia de habla inglesa, utilice el enlace a la traducci贸n: Maxim Zvyagintsev. C贸mo configurar PVS-Studio en Travis CI usando el ejemplo del emulador de consola de juegos PSP .

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


All Articles