Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP
Travis CI je distribuovaná webová služba na vytváranie a testovanie softvéru, ktorá využíva GitHub ako hosting zdrojového kódu. Okrem vyššie uvedených operačných scenárov si môžete pridať svoje vlastné vďaka rozsiahlym možnostiam konfigurácie. V tomto článku nakonfigurujeme Travis CI na prácu s PVS-Studio pomocou príkladu kódu PPSSPP.

Úvod

Travis C.I. je webová služba na vytváranie a testovanie softvéru. Zvyčajne sa používa v spojení s postupmi nepretržitej integrácie.

PPSSPP - Emulátor hernej konzoly PSP. Program je schopný emulovať spúšťanie akýchkoľvek hier z diskových obrazov určených pre Sony PSP. Program bol vydaný 1. novembra 2012. PPSSPP je licencovaný pod GPL v2. Každý môže urobiť zlepšenie zdrojový kód projektu.

Štúdio PVS — analyzátor statického kódu na vyhľadávanie chýb a potenciálnych zraniteľností v programovom kóde. V tomto článku pre zmenu spustíme PVS-Studio nie lokálne na počítači vývojára, ale v cloude a budeme hľadať chyby v PPSSPP.

Nastavenie Travis CI

Budeme potrebovať úložisko na GitHub, kde sa nachádza projekt, ktorý potrebujeme, ako aj kľúč pre PVS-Studio (môžete získať skúšobný kľúč alebo zadarmo pre Open Source projekty).

Poďme na stránku Travis C.I.. Po autorizácii pomocou vášho účtu GitHub sa nám zobrazí zoznam úložísk:

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP
Na test som forkoval PPSSPP.

Aktivujeme úložisko, ktoré chceme zhromaždiť:

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP
V súčasnosti nemôže Travis CI postaviť náš projekt, pretože neexistujú žiadne pokyny na stavbu. Takže je čas na konfiguráciu.

Pri analýze sa nám budú hodiť niektoré premenné, napríklad kľúč pre PVS-Studio, ktorý by bolo nežiaduce špecifikovať v konfiguračnom súbore. Pridajme teda premenné prostredia pomocou nastavení zostavy v Travis CI:

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP
potrebujeme:

  • PVS_USERNAME – používateľské meno
  • PVS_KEY - kľúč
  • MAIL_USER - e-mail, ktorý bude použitý na odoslanie prehľadu
  • MAIL_PASSWORD - e-mailové heslo

Posledné dve sú voliteľné. Tieto budú použité na odoslanie výsledkov poštou. Ak chcete správu distribuovať iným spôsobom, nemusíte ich uvádzať.

Takže sme pridali premenné prostredia, ktoré potrebujeme:

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP
Teraz vytvoríme súbor .travis.yml a umiestnite ho do koreňového adresára projektu. PPSSPP už mal konfiguračný súbor pre Travis CI, bol však príliš veľký a pre príklad úplne nevhodný, takže sme ho museli výrazne zjednodušiť a ponechať len základné prvky.

Najprv označme jazyk, verziu Ubuntu Linux, ktorú chceme použiť vo virtuálnom stroji, a potrebné balíčky pre zostavenie:

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'

Všetky uvedené balíčky sú potrebné výhradne pre PPSSPP.

Teraz uvádzame montážnu maticu:

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

Trochu viac o sekcii matice. V Travis CI existujú dva spôsoby vytvárania možností zostavenia: prvým je špecifikácia zoznamu kompilátorov, typov operačného systému, premenných prostredia atď., po čom sa vygeneruje matica všetkých možných kombinácií; druhý je explicitný údaj o matici. Tieto dva prístupy môžete samozrejme kombinovať a pridať jedinečný prípad, alebo ho naopak pomocou sekcie vylúčiť vylúčiť. Viac si o tom môžete prečítať v Dokumentácia Travis CI.

Zostáva len poskytnúť návod na montáž pre konkrétny projekt:

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 vám umožňuje pridávať vlastné príkazy pre rôzne fázy života virtuálneho počítača. oddiel pred_inštaláciou spustené pred inštaláciou balíkov. Potom inštalovať, ktorá nasleduje po inštalácii balíkov zo zoznamu addons.aptktoré sme naznačili vyššie. Samotná montáž prebieha v scenár. Ak všetko pôjde dobre, ocitneme sa tam po_úspechu (v tejto časti spustíme statickú analýzu). Toto nie sú všetky kroky, ktoré možno upraviť, ak potrebujete viac, mali by ste sa pozrieť Dokumentácia Travis CI.

Pre uľahčenie čítania boli príkazy umiestnené v samostatnom skripte .travis.sh, ktorý je umiestnený v koreňovom adresári projektu.

Takže máme nasledujúci súbor .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

Pred inštaláciou balíkov aktualizujeme submoduly. To je potrebné na vybudovanie PPSSPP. Pridajme prvú funkciu do .travis.sh (všimnite si rozšírenie):

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

Teraz prejdeme priamo k nastaveniu automatického spustenia PVS-Studio v Travis CI. Najprv musíme do systému nainštalovať balík PVS-Studio:

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
}

Na začiatku funkcie travis_install nainštalujeme kompilátory, ktoré potrebujeme, pomocou premenných prostredia. Potom ak premenná $ PVS_ANALYZE uchováva hodnotu Áno (uviedli sme to v sekcii ca. počas konfigurácie zostavovacej matice), nainštalujeme balík pvs-štúdio. Okrem toho sú uvedené aj balíčky libio-socket-ssl-perl и libnet-ssleay-perl, sú však potrebné na odosielanie výsledkov poštou, takže nie sú potrebné, ak ste si vybrali iný spôsob doručenia správy.

Funkcia download_extract stiahne a rozbalí zadaný archív:

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

Je čas dať projekt dokopy. Toto sa deje v sekcii scenár:

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
}

V skutočnosti ide o zjednodušenú pôvodnú konfiguráciu, s výnimkou týchto riadkov:

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

V tejto časti kódu sme si stanovili cmake príznak pre export kompilačných príkazov. Toto je potrebné pre analyzátor statického kódu. Viac si o tom môžete prečítať v článku „Ako spustiť PVS-Studio na Linuxe a MacOS".

Ak bola montáž úspešná, tak sa dostávame do po_úspechu, kde vykonávame statickú analýzu:

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
}

Pozrime sa bližšie na nasledujúce riadky:

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

Prvý riadok vygeneruje licenčný súbor z používateľského mena a kľúča, ktoré sme zadali na úplnom začiatku pri nastavovaní premenných prostredia Travis CI.

Druhý riadok spustí analýzu priamo. Vlajka -j nastavuje počet vlákien na analýzu, príznak -l označuje licenciu, vlajku -o definuje súbor pre výstup protokolov a príznak -disableLicenseExpirationCheck vyžadované pre skúšobné verzie, pretože štandardne pvs-studio-analyzer upozorní používateľa, že platnosť licencie čoskoro vyprší. Aby ste tomu zabránili, môžete zadať tento príznak.

Protokolový súbor obsahuje nespracovaný výstup, ktorý nie je možné prečítať bez konverzie, takže najprv musíte súbor sprístupniť. Prejdeme cez polená plog-konvertora výstupom je súbor html.

V tomto príklade som sa rozhodol posielať správy poštou pomocou príkazu poslať email.

V dôsledku toho sme dostali nasledujúci súbor .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;

Teraz je čas presunúť zmeny do úložiska git, po ktorom Travis CI automaticky spustí zostavenie. Kliknutím na „ppsspp“ prejdete na zostavy zostavy:

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP
Uvidíme prehľad aktuálnej zostavy:

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP
Ak je zostava úspešne dokončená, dostaneme e-mail s výsledkami statickej analýzy. Poštová zásielka samozrejme nie je jediným spôsobom, ako dostať správu. Môžete si vybrať akýkoľvek spôsob implementácie. Je však dôležité si uvedomiť, že po dokončení zostavenia nebude možné pristupovať k súborom virtuálneho počítača.

Súhrn chýb

Najťažšiu časť máme úspešne za sebou. Teraz sa presvedčte, že všetko naše úsilie stojí za to. Pozrime sa na niektoré zaujímavé body zo správy o statickej analýze, ktorá mi prišla poštou (nie nadarmo som to naznačil).

Nebezpečná optimalizácia

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

Upozornenie PVS-Studio: V597 Kompilátor mohol vymazať volanie funkcie 'memset', ktoré sa používa na vyprázdnenie vyrovnávacej pamäte 'súčet'. Na vymazanie súkromných údajov by sa mala použiť funkcia RtlSecureZeroMemory(). sha1.cpp 325

Tento kus kódu sa nachádza v zabezpečenom hašovacom module, obsahuje však závažnú bezpečnostnú chybu (CWE-14). Pozrime sa na výpis zostavy, ktorý sa generuje pri kompilácii verzie ladenia:

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

Všetko je v poriadku a funkcia memeset sa vykoná, čím sa prepíšu dôležité dáta v RAM, no zatiaľ sa neradujte. Pozrime sa na zoznam zostavy verzie Release s optimalizáciou:

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

Ako vidno z výpisu, kompilátor volanie ignoroval memeset. Je to spôsobené tým, že vo funkcii sha1 po hovore memeset už žiadny odkaz na štruktúru ctx. Preto kompilátor nevidí zmysel v plytvaní časom procesora prepisovaním pamäte, ktorá sa v budúcnosti nepoužije. Môžete to opraviť pomocou funkcie RtlSecureZeroMemory alebo podobný jej.

opraviť:

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

Zbytočné porovnávanie

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

Upozornenie PVS-Studio: V547 Výraz 'leftvol >= 0' je vždy pravdivý. sceAudio.cpp 120

Najprv venujte pozornosť vetve else if. Kód bude vykonaný, iba ak sú splnené všetky podmienky leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0 sa ukáže ako falošný. Preto dostaneme nasledujúce tvrdenia, ktoré budú platiť pre vetvu else: leftvol <= 0xFFFF, rightvol <= 0xFFFF, leftvol >= 0 и rightvol >= 0. Všimnite si posledné dva výroky. Má zmysel kontrolovať, čo je nevyhnutnou podmienkou pre vykonanie tohto kusu kódu?

Takže môžeme bezpečne odstrániť tieto podmienené príkazy:

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

Ďalší scenár. Za týmito nadbytočnými podmienkami sa skrýva nejaký druh chyby. Možno neskontrolovali, čo bolo potrebné.

Ctrl+C Ctrl+V vracia úder

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 Naľavo a napravo od výrazu '||' sú rovnaké podvýrazy '!Memory::IsValidAddress(psmfData)' operátor. scePsmf.cpp 703

Venujte pozornosť kontrole vo vnútri if. Nezdá sa vám zvláštne, že kontrolujeme, či je adresa platná? psmfData, dva krát toľko? Tak toto sa mi zdá zvláštne... V skutočnosti ide samozrejme o preklep a zámerom bolo skontrolovať oba vstupné parametre.

Správna možnosť:

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

Zabudnutá premenná

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

Upozornenie PVS-Studio: V547 Výraz 'veľkosť == 8' je vždy nepravdivý. syn-att.c 195

Táto chyba sa nachádza v priečinku ext, takže to naozaj nie je relevantné pre projekt, ale chyba sa našla skôr, ako som si ju všimol, tak som sa rozhodol ju nechať. Koniec koncov, tento článok nie je o kontrole chýb, ale o integrácii s Travis CI a nebola vykonaná žiadna konfigurácia analyzátora.

Variabilné veľkosť sa inicializuje konštantou, tá sa však v kóde vôbec nepoužíva, až po operátora if, čo, samozrejme, dáva nepravdivý pri kontrole podmienok, pretože, ako si pamätáme, veľkosť rovná nule. Následné kontroly tiež nemajú zmysel.

Autor fragmentu kódu zrejme zabudol prepísať premennú veľkosť pred tým.

Stop

Tu asi s chybami skončíme. Účelom tohto článku je demonštrovať prácu PVS-Studio spolu s Travis CI a nie čo najdôkladnejšie analyzovať projekt. Ak chcete väčšie a krajšie chyby, vždy ich môžete obdivovať tu :).

Záver

Používanie webových služieb na vytváranie projektov spolu s praxou prírastkovej analýzy vám umožňuje nájsť veľa problémov ihneď po zlúčení kódu. Jedno zostavenie však nemusí stačiť, takže nastavenie testovania spolu so statickou analýzou výrazne zlepší kvalitu kódu.

Užitočné odkazy

Ako nakonfigurovať PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP

Ak chcete tento článok zdieľať s anglicky hovoriacim publikom, použite odkaz na preklad: Maxim Zvjagincev. Ako nastaviť PVS-Studio v Travis CI pomocou príkladu emulátora hernej konzoly PSP.

Zdroj: hab.com

Pridať komentár