Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Travis CI je distribuirani web servis za izradu i testiranje softvera koji koristi GitHub kao hosting izvornog koda. Pored gore navedenih operativnih scenarija, možete dodati svoje vlastite zahvaljujući opsežnim opcijama konfiguracije. U ovom članku ćemo konfigurirati Travis CI za rad sa PVS-Studio koristeći primjer PPSSPP koda.

Uvod

Travis C.I. je web servis za izradu i testiranje softvera. Obično se koristi zajedno sa praksama kontinuirane integracije.

PPSSPP - Emulator PSP igraće konzole. Program je u stanju emulirati pokretanje bilo koje igre sa slika diska namijenjenih Sony PSP-u. Program je objavljen 1. novembra 2012. godine. PPSSPP je licenciran pod GPL v2. Svako može poboljšati izvorni kod projekta.

PVS Studio — statički analizator koda za traženje grešaka i potencijalnih ranjivosti u programskom kodu. U ovom članku, za promjenu, pokrenut ćemo PVS-Studio ne lokalno na stroju programera, već u oblaku i potražiti greške u PPSSPP-u.

Postavljanje Travis CI

Trebat će nam spremište na GitHubu, gdje se nalazi projekt koji nam je potreban, kao i ključ za PVS-Studio (možete nabaviti probni ključ ili besplatno za projekte otvorenog koda).

Idemo na stranicu Travis C.I.. Nakon autorizacije pomoću vašeg GitHub naloga, vidjet ćemo listu spremišta:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Za test sam račvao PPSSPP.

Aktiviramo spremište koje želimo prikupiti:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Trenutačno, Travis CI ne može izgraditi naš projekt jer ne postoje upute za izgradnju. Dakle, vrijeme je za konfiguraciju.

Tokom analize, neke varijable će nam biti korisne, na primjer, ključ za PVS-Studio, koji bi bilo nepoželjno navesti u konfiguracijskoj datoteci. Dakle, dodajmo varijable okruženja koristeći postavke izgradnje u Travis CI:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Trebaće nam:

  • PVS_USERNAME - korisničko ime
  • PVS_KEY - ključ
  • MAIL_USER - email koji će se koristiti za slanje izvještaja
  • MAIL_PASSWORD - lozinka e-pošte

Posljednja dva su opciona. Oni će se koristiti za slanje rezultata poštom. Ako želite da distribuirate izvještaj na drugi način, ne morate ih naznačiti.

Dakle, dodali smo varijable okruženja koje su nam potrebne:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Sada kreirajmo fajl .travis.yml i stavite ga u korijen projekta. PPSSPP je već imao konfiguracionu datoteku za Travis CI, međutim, ona je bila prevelika i potpuno neprikladna za primjer, pa smo je morali uvelike pojednostaviti i ostaviti samo osnovne elemente.

Prvo, naznačimo jezik, verziju Ubuntu Linuxa koju želimo da koristimo u virtuelnoj mašini i potrebne pakete za izgradnju:

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'

Svi navedeni paketi su potrebni isključivo za PPSSPP.

Sada označavamo matricu sklopa:

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

Malo više o sekciji matrica. U Travis CI-ju postoje dva načina za kreiranje opcija izgradnje: prvi je specificiranje liste kompajlera, tipova operativnog sistema, varijabli okruženja, itd., nakon čega se generiše matrica svih mogućih kombinacija; drugi je eksplicitna indikacija matrice. Naravno, možete kombinirati ova dva pristupa i dodati jedinstveni slučaj, ili, naprotiv, isključiti ga pomoću odjeljka isključi. Više o ovome možete pročitati u Travis CI dokumentacija.

Sve što preostaje je pružiti upute za montažu specifične za projekat:

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 vam omogućava da dodate sopstvene komande za različite faze života virtuelne mašine. Odjeljak before_install izvršava se prije instaliranja paketa. Onda instalirajte, koji prati instalaciju paketa sa liste addons.aptkoje smo gore naznačili. Sama montaža se odvija u skripta. Ako je sve prošlo kako treba, onda se nalazimo u njemu after_success (u ovom odeljku ćemo pokrenuti statičku analizu). Ovo nisu svi koraci koji se mogu mijenjati, ako vam je potrebno više, onda biste trebali pogledati Travis CI dokumentacija.

Radi lakšeg čitanja, komande su stavljene u posebnu skriptu .travis.sh, koji se nalazi u korijenu projekta.

Dakle, imamo sljedeći fajl .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

Prije instaliranja paketa, ažurirat ćemo podmodule. Ovo je potrebno za izgradnju PPSSPP. Dodajmo prvu funkciju .travis.sh (obratite pažnju na ekstenziju):

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

Sada dolazimo direktno do podešavanja automatskog pokretanja PVS-Studio u Travis CI. Prvo moramo da instaliramo PVS-Studio paket na sistem:

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 početku funkcije travis_install mi instaliramo kompajlere koji su nam potrebni koristeći varijable okruženja. Zatim ako je varijabla $PVS_ANALYZE skladišti vrijednost Da (naznačili smo to u odjeljku env tokom konfiguracije matrice izgradnje), instaliramo paket pvs-studio. Pored toga, naznačeni su i paketi libio-socket-ssl-perl и libnet-ssleay-perl, međutim, oni su potrebni za slanje rezultata poštom, tako da nisu potrebni ako ste odabrali drugi način za dostavu izvještaja.

funkcija download_extract preuzima i raspakuje navedenu arhivu:

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

Vrijeme je da sastavite projekat. Ovo se dešava u sekciji skripta:

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
}

Zapravo, ovo je pojednostavljena originalna konfiguracija, osim ovih linija:

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

U ovom dijelu koda smo postavili za cmake zastavicu za izvoz naredbi kompilacije. Ovo je neophodno za statički analizator koda. Više o tome možete pročitati u članku “Kako pokrenuti PVS-Studio na Linuxu i macOS-u".

Ako je skupština bila uspješna, dolazimo do toga after_success, gdje vršimo statičku analizu:

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
}

Pogledajmo bliže sljedeće redove:

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

Prvi red generiše licencni fajl od korisničkog imena i ključa koje smo naveli na samom početku prilikom podešavanja varijabli okruženja Travis CI.

Drugi red direktno započinje analizu. Zastava -j postavlja broj niti za analizu, zastavicu -l označava dozvolu, zastavu -o definira datoteku za izlaz dnevnika i zastavicu -disableLicenseExpirationCheck potrebno za probne verzije, jer je po defaultu pvs-studio-analyzer će upozoriti korisnika da će licenca uskoro isteći. Da biste spriječili da se to dogodi, možete odrediti ovu zastavicu.

Datoteka evidencije sadrži sirovi izlaz koji se ne može pročitati bez konverzije, tako da prvo morate učiniti datoteku čitljivom. Hajde da prođemo kroz trupce plog-konverter, a izlaz je html datoteka.

U ovom primjeru, odlučio sam da šaljem izvještaje poštom koristeći naredbu pošaljite e-mail.

Kao rezultat, dobili smo sljedeći fajl .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;

Sada je vrijeme da se promjene uguraju u git spremište, nakon čega će Travis CI automatski pokrenuti build. Kliknite na “ppsspp” da biste otišli na izvještaje o izgradnji:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Vidjet ćemo pregled trenutne gradnje:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Ako je gradnja uspješno završena, primit ćemo e-poštu s rezultatima statičke analize. Naravno, slanje putem pošte nije jedini način za primanje izvještaja. Možete odabrati bilo koju metodu implementacije. Ali važno je zapamtiti da nakon završetka izgradnje neće biti moguće pristupiti datotekama virtuelne mašine.

Sažetak greške

Uspješno smo završili najteži dio. Sada se uvjerimo da je sav naš trud vrijedan toga. Pogledajmo neke zanimljive tačke iz izvještaja o statičkoj analizi koji mi je stigao poštom (nisam uzalud to naznačio).

Opasna optimizacija

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 upozorenje: V597 Kompajler bi mogao izbrisati poziv funkcije 'memset', koji se koristi za ispiranje bafera 'sum'. Funkciju RtlSecureZeroMemory() treba koristiti za brisanje privatnih podataka. sha1.cpp 325

Ovaj dio koda nalazi se u sigurnom heš modulu, međutim, sadrži ozbiljnu sigurnosnu grešku (CWE-14). Pogledajmo popis sklopova koji se generira prilikom kompajliranja Debug verzije:

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

Sve je u redu i funkcija memeset se izvršava, čime se prepisuju važni podaci u RAM, međutim, nemojte se još radovati. Pogledajmo popis sklopa verzije Release s optimizacijom:

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

Kao što se vidi iz listinga, kompajler je ignorisao poziv memeset. To je zbog činjenice da je u funkciji sha1 nakon poziva memeset nema više pozivanja na strukturu ctx. Stoga, kompajler ne vidi smisao u trošenju procesorskog vremena na prepisivanje memorije koja se neće koristiti u budućnosti. Ovo možete popraviti korištenjem funkcije RtlSecureZeroMemory ili slično njoj.

Tačno:

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

Nepotrebno poređenje

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 upozorenje: V547 Izraz 'leftvol >= 0' je uvijek tačan. sceAudio.cpp 120

Prvo obratite pažnju na granu else if. Kod će biti izvršen samo ako su ispunjeni svi uslovi leftvol > 0xFFFF || desni vol > 0xFFFF || leftvol < 0 || desni vol < 0 ispostaviće se da je lažno. Dakle, dobijamo sledeće izjave, koje će važiti za granu else: leftvol <= 0xFFFF, desni vol <= 0xFFFF, levi vol >= 0 и desni vol >= 0. Obratite pažnju na poslednje dve izjave. Ima li smisla provjeriti koji je neophodan uslov za izvršenje ovog dijela koda?

Dakle, možemo bezbedno ukloniti ove uslovne izjave:

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

Drugi scenario. Iza ovih suvišnih uslova krije se neka vrsta greške. Možda nisu provjerili šta je potrebno.

Ctrl+C Ctrl+V uzvraća udarac

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 Postoje identični podizrazi '!Memory::IsValidAddress(psmfData)' lijevo i desno od '||' operater. scePsmf.cpp 703

Obratite pažnju na ček iznutra if. Ne mislite li da je čudno što provjeravamo da li je adresa važeća? psmfData, duplo više? Tako da mi se ovo čini čudnim... U stvari, ovo je, naravno, greška u kucanju, a ideja je bila da se provere oba ulazna parametra.

Ispravna opcija:

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

Zaboravljena varijabla

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 upozorenje: V547 Izraz 'size == 8' je uvijek netačan. syn-att.c 195

Ova greška se nalazi u folderu ext, tako da nije baš relevantno za projekat, ali je greška pronađena prije nego što sam je primijetio, pa sam odlučio da je ostavim. Uostalom, ovaj članak nije o pregledu grešaka, već o integraciji sa Travis CI, a konfiguracija analizatora nije izvršena.

Promjenjivo veličina je inicijaliziran konstantom, međutim, uopće se ne koristi u kodu, sve do operatora if, što, naravno, daje lažan pri provjeravanju uslova, jer, kako se sjećamo, veličina jednaka nuli. Naknadne provjere također nemaju smisla.

Očigledno je autor fragmenta koda zaboravio da prepiše promenljivu veličina pre toga.

Stop

Tu ćemo vjerovatno završiti sa greškama. Svrha ovog članka je da demonstrira rad PVS-Studio zajedno sa Travis CI, a ne da što detaljnije analizira projekat. Ako želite veće i ljepše greške, uvijek im se možete diviti ovdje :).

zaključak

Korištenje web servisa za izgradnju projekata zajedno s praksom inkrementalne analize omogućava vam da pronađete mnoge probleme odmah nakon spajanja koda. Međutim, jedna gradnja možda neće biti dovoljna, pa će postavljanje testiranja zajedno sa statičkom analizom značajno poboljšati kvalitet koda.

korisni linkovi

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole

Ako želite da podijelite ovaj članak sa publikom koja govori engleski, koristite link za prijevod: Maxim Zvyagintsev. Kako postaviti PVS-Studio u Travis CI na primjeru emulatora PSP igraće konzole.

izvor: www.habr.com

Dodajte komentar