Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator
Travis CI är en distribuerad webbtjänst för att bygga och testa programvara som använder GitHub som källkodsvärd. Förutom ovanstående driftscenarier kan du lägga till dina egna tack vare de omfattande konfigurationsalternativen. I den här artikeln kommer vi att konfigurera Travis CI att arbeta med PVS-Studio med hjälp av PPSSPP-kodexemplet.

Inledning

Travis CI är en webbtjänst för att bygga och testa mjukvara. Det används vanligtvis i samband med kontinuerliga integrationsmetoder.

PPSSPP - PSP-spelkonsolemulator. Programmet kan efterlikna lanseringen av alla spel från diskbilder avsedda för Sony PSP. Programmet släpptes den 1 november 2012. PPSSPP är licensierad under GPL v2. Vem som helst kan göra förbättringar projektets källkod.

PVS-studio — en statisk kodanalysator för att söka efter fel och potentiella sårbarheter i programkod. I den här artikeln kommer vi för en förändring att starta PVS-Studio inte lokalt på utvecklarens maskin, utan i molnet, och leta efter fel i PPSSPP.

Konfigurera Travis CI

Vi kommer att behöva ett arkiv på GitHub, där projektet vi behöver finns, samt en nyckel för PVS-Studio (du kan få testnyckel eller gratis för Open Source-projekt).

Låt oss gå till webbplatsen Travis CI. Efter auktorisering med ditt GitHub-konto kommer vi att se en lista med förråd:

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator
För testet gaffla jag PPSSPP.

Vi aktiverar arkivet som vi vill samla in:

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator
För tillfället kan Travis CI inte bygga vårt projekt eftersom det inte finns några instruktioner för att bygga. Så det är dags för konfiguration.

Under analysen kommer vissa variabler att vara användbara för oss, till exempel nyckeln för PVS-Studio, vilket skulle vara oönskat att specificera i konfigurationsfilen. Så låt oss lägga till miljövariabler med hjälp av bygginställningarna i Travis CI:

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator
Vi behöver:

  • PVS_USERNAME - användarnamn
  • PVS_KEY - nyckel
  • MAIL_USER - e-post som kommer att användas för att skicka rapporten
  • MAIL_PASSWORD - e-postlösenord

De två sista är valfria. Dessa kommer att användas för att skicka resultat via post. Om du vill distribuera rapporten på annat sätt behöver du inte ange dem.

Så vi har lagt till de miljövariabler vi behöver:

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator
Låt oss nu skapa en fil .travis.yml och placera den i roten av projektet. PPSSPP hade redan en konfigurationsfil för Travis CI, men den var för stor och helt olämplig för exemplet, så vi var tvungna att förenkla den kraftigt och bara lämna de grundläggande elementen.

Låt oss först ange språket, versionen av Ubuntu Linux som vi vill använda i den virtuella maskinen och de nödvändiga paketen för bygget:

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'

Alla paket som är listade behövs exklusivt för PPSSPP.

Nu anger vi monteringsmatrisen:

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

Lite mer om avsnittet matris. I Travis CI finns det två sätt att skapa byggalternativ: det första är att specificera en lista med kompilatorer, operativsystemtyper, miljövariabler etc., varefter en matris med alla möjliga kombinationer genereras; den andra är en explicit indikation på matrisen. Naturligtvis kan du kombinera dessa två tillvägagångssätt och lägga till ett unikt fall, eller tvärtom, utesluta det med hjälp av avsnittet utesluta. Du kan läsa mer om detta i Travis CI-dokumentation.

Allt som återstår är att tillhandahålla projektspecifika monteringsanvisningar:

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 låter dig lägga till dina egna kommandon för olika stadier av en virtuell maskins liv. Sektion före_installera körs innan du installerar paket. Sedan installera, som följer installationen av paket från listan addons.aptsom vi angav ovan. Själva monteringen sker i skript. Om allt gick bra, då befinner vi oss i efter_framgång (det är i detta avsnitt som vi kommer att köra statisk analys). Det här är inte alla steg som kan modifieras, behöver du fler så ska du titta in Travis CI-dokumentation.

För att underlätta läsningen placerades kommandona i ett separat skript .travis.sh, som placeras vid projektroten.

Så vi har följande fil .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

Innan vi installerar paketen kommer vi att uppdatera undermodulerna. Detta behövs för att bygga PPSSPP. Låt oss lägga till den första funktionen till .travis.sh (observera tillägget):

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

Nu kommer vi direkt till att sätta upp den automatiska lanseringen av PVS-Studio i Travis CI. Först måste vi installera PVS-Studio-paketet på systemet:

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
}

I början av funktionen travis_install vi installerar kompilatorerna vi behöver med hjälp av miljövariabler. Sedan om variabeln $PVS_ANALYZE lagrar värde Ja (vi angav det i avsnittet env under byggmatriskonfiguration) installerar vi paketet pvs-studio. Utöver detta anges även paket libio-socket-ssl-perl и libnet-ssleay-perl, men de krävs för att skicka resultat, så de är inte nödvändiga om du har valt en annan metod för att leverera din rapport.

Funktion download_extract laddar ner och packar upp det angivna arkivet:

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

Det är dags att sätta ihop projektet. Detta händer i avsnittet skript:

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
}

Faktum är att detta är en förenklad originalkonfiguration, förutom dessa rader:

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

I det här avsnittet av koden ställer vi in ​​för cmmake flagga för export av kompileringskommandon. Detta är nödvändigt för en statisk kodanalysator. Du kan läsa mer om detta i artikeln "Hur man kör PVS-Studio på Linux och macOS".

Om monteringen lyckades, då kommer vi till efter_framgång, där vi utför statisk analys:

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 [email protected] 
              -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
}

Låt oss titta närmare på följande rader:

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

Den första raden genererar en licensfil från användarnamnet och nyckeln som vi angav i början när vi satte upp Travis CI-miljövariablerna.

Den andra raden startar analysen direkt. Flagga -j ställer in antalet trådar för analys, flagga -l anger licens, flagga -o definierar filen för utmatning av loggar och flaggan -disableLicenseExpirationCheck krävs för testversioner, eftersom som standard pvs-studio-analysator kommer att varna användaren om att licensen är på väg att löpa ut. För att förhindra att detta händer kan du ange denna flagga.

Loggfilen innehåller råutdata som inte kan läsas utan konvertering, så du måste först göra filen läsbar. Låt oss passera stockarna plog-omvandlare, och utdata är en html-fil.

I det här exemplet bestämde jag mig för att skicka rapporter per post med kommandot skicka epost.

Som ett resultat fick vi följande fil .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 [email protected] 
              -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;

Nu är det dags att driva ändringarna i git-förvaret, varefter Travis CI kommer att köra bygget automatiskt. Klicka på "ppsspp" för att gå till byggrapporterna:

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator
Vi kommer att se en översikt över den nuvarande konstruktionen:

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator
Om bygget slutförs framgångsrikt kommer vi att få ett e-postmeddelande med resultaten av den statiska analysen. Utskick är naturligtvis inte det enda sättet att få en rapport. Du kan välja vilken implementeringsmetod som helst. Men det är viktigt att komma ihåg att efter att konstruktionen är klar kommer det inte att vara möjligt att komma åt de virtuella maskinfilerna.

Felsammanfattning

Vi har framgångsrikt slutfört den svåraste delen. Låt oss nu se till att alla våra ansträngningar är värda det. Låt oss titta på några intressanta punkter från den statiska analysrapporten som kom till mig per post (det var inte för inte som jag angav det).

Farlig optimering

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 ) );
}

PVS-Studio varning: V597 Kompilatorn skulle kunna ta bort "memset" funktionsanropet, som används för att tömma "summa" buffert. Funktionen RtlSecureZeroMemory() ska användas för att radera privata data. sha1.cpp 325

Denna kodbit finns i den säkra hashmodulen, men den innehåller ett allvarligt säkerhetsfel (CWE-14). Låt oss titta på monteringslistan som genereras när du kompilerar Debug-versionen:

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

Allt är i sin ordning och fungerar memeset exekveras och skrivs därmed över viktig data i RAM-minnet, men gläd dig inte ännu. Låt oss titta på monteringslistan för releaseversionen med optimering:

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

Som framgår av listan ignorerade kompilatorn anropet memeset. Detta beror på det faktum att i funktionen sha1 efter samtalet memeset ingen mer hänvisning till struktur ctx. Därför ser kompilatorn ingen mening med att slösa bort processortid på att skriva över minne som inte används i framtiden. Du kan fixa detta genom att använda funktionen RtlSecureZeroMemory eller en liknande till henne.

korrigera:

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 ) );
} 

Onödig jämförelse

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);
  }
}

PVS-Studio varning: V547 Uttrycket 'vänstervol >= 0' är alltid sant. sceAudio.cpp 120

Var uppmärksam på den andra grenen för den första if. Koden kommer endast att köras om alla villkor leftvol > 0xFFFF || högervolym > 0xFFFF || leftvol < 0 || högervolym < 0 kommer att visa sig vara falskt. Därför får vi följande påståenden, vilket kommer att vara sant för den andra grenen: leftvol <= 0xFFFF, rightvol <= 0xFFFF, vänstervolym >= 0 и högervolym >= 0. Lägg märke till de två sista påståendena. Är det vettigt att kontrollera vad som är ett nödvändigt villkor för exekvering av denna kod?

Så vi kan säkert ta bort dessa villkorliga uttalanden:

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);
  }
}

Ett annat scenario. Det finns något slags fel gömt bakom dessa redundanta villkor. Kanske kollade de inte vad som krävdes.

Ctrl+C Ctrl+V Slår tillbaka

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 Det finns identiska underuttryck '!Memory::IsValidAddress(psmfData)' till vänster och till höger om '||' operatör. scePsmf.cpp 703

Var uppmärksam på checken inuti if. Tycker du inte att det är konstigt att vi kollar om adressen är giltig? psmfData, dubbelt så mycket? Så det här verkar konstigt för mig... I själva verket är detta naturligtvis ett stavfel, och tanken var att kontrollera båda inmatningsparametrarna.

Rätt alternativ:

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

Glömd variabel

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");
  }
  ....
}

PVS-Studio varning: V547 Uttrycket 'storlek == 8' är alltid falskt. syn-att.c 195

Detta fel finns i mappen ext, så inte riktigt relevant för projektet, men buggen hittades innan jag märkte den, så jag bestämde mig för att lämna den. När allt kommer omkring handlar den här artikeln inte om granskning av fel, utan om integration med Travis CI, och ingen konfiguration av analysatorn utfördes.

Variabel Storlek initieras av en konstant, men den används inte alls i koden, ända ner till operatören if, vilket naturligtvis ger falsk medan vi kontrollerar villkoren, eftersom, som vi minns, Storlek lika med noll. Efterföljande kontroller är inte heller meningsfulla.

Tydligen glömde författaren till kodfragmentet att skriva över variabeln Storlek före det.

Sluta

Det är här vi förmodligen kommer att sluta med misstagen. Syftet med denna artikel är att demonstrera PVS-Studios arbete tillsammans med Travis CI, och inte att analysera projektet så noggrant som möjligt. Vill du ha större och vackrare misstag kan du alltid beundra dem här :).

Slutsats

Genom att använda webbtjänster för att bygga projekt tillsammans med övningen av inkrementell analys kan du hitta många problem omedelbart efter sammanslagning av kod. Det kan dock hända att en konstruktion inte räcker, så att sätta upp testning tillsammans med statisk analys kommer att avsevärt förbättra kodens kvalitet.

Användbara länkar

Hur man konfigurerar PVS-Studio i Travis CI med hjälp av exemplet med en PSP-spelkonsolemulator

Om du vill dela den här artikeln med en engelsktalande publik, använd gärna översättningslänken: Maxim Zvyagintsev. Hur man ställer in PVS-Studio i Travis CI med hjälp av exemplet med PSP-spelkonsolemulatorn.

Källa: will.com

Lägg en kommentar