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 distribuirana web usluga za izradu i testiranje softvera koja koristi GitHub kao hosting izvornog koda. Uz gore navedene scenarije rada, možete dodati svoje vlastite zahvaljujući opsežnim mogućnostima konfiguracije. U ovom ćemo članku konfigurirati Travis CI za rad s PVS-Studiom koristeći primjer koda PPSSPP.

Uvod

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

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

PVS-Studio — statički analizator koda za traženje pogreš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 tražiti pogreške u PPSSPP-u.

Postavljanje Travis CI

Trebat će nam repozitorij na GitHubu, gdje se nalazi projekt koji nam je potreban, kao i ključ za PVS-Studio (možete dobiti probni ključ ili besplatno za Open Source projekte).

Idemo na mjesto Travis C.I.. Nakon autorizacije pomoću vašeg GitHub računa, vidjet ćemo popis repozitorija:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Za test sam forkovao 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 nema uputa za izgradnju. Dakle, vrijeme je za konfiguraciju.

Tijekom 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
Trebat ćemo:

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

Posljednje dvije su izborne. Oni će se koristiti za slanje rezultata poštom. Ako izvješće želite distribuirati na drugi način, ne morate ih označavati.

Dakle, dodali smo varijable okoline koje su nam potrebne:

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Kreirajmo sada datoteku .travis.yml i smjestite ga u korijen projekta. PPSSPP je već imao konfiguracijsku datoteku za Travis CI, međutim, bila je prevelika i potpuno neprikladna za primjer, pa smo je morali jako pojednostaviti i ostaviti samo osnovne elemente.

Prvo, označimo jezik, verziju Ubuntu Linuxa koju želimo koristiti u virtualnom stroju 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

Još malo o rubrici matrica. U Travis CI-u postoje dva načina za kreiranje opcija izgradnje: prvi je određivanje popisa prevoditelja, tipova operativnih sustava, varijabli okruženja itd., nakon čega se generira matrica svih mogućih kombinacija; drugi je eksplicitna indikacija matrice. Naravno, možete kombinirati ova dva pristupa i dodati jedinstveni slučaj ili ga, naprotiv, isključiti pomoću odjeljka isključiti. 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 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 vam omogućuje dodavanje vlastitih naredbi za različite faze života virtualnog stroja. Odjeljak prije_instalacije izvršiti prije instaliranja paketa. Zatim instalirati, koji prati instalaciju paketa s liste dodaci.aptkoje smo gore naznačili. Sama skupština odvija se u rukopis. Ako je sve prošlo dobro, onda se nalazimo unutra nakon_uspjeha (u ovom odjeljku ćemo provesti statičku analizu). Ovo nisu svi koraci koji se mogu mijenjati, ako trebate više, trebali biste pogledati Travis CI dokumentacija.

Radi lakšeg čitanja, naredbe su smještene u zasebnu skriptu .travis.sh, koji se nalazi u korijenu projekta.

Dakle, imamo sljedeću datoteku .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-a. Dodajmo prvu funkciju u .travis.sh (obratite pažnju na ekstenziju):

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

Sada dolazimo izravno do postavljanja automatskog pokretanja PVS-Studio u Travis CI. Prvo moramo instalirati PVS-Studio paket na sustav:

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 instaliramo prevoditelje koji su nam potrebni pomoću varijabli okruženja. Tada ako varijabla $PVS_ANALYZE pohranjuje vrijednost Da (naveli smo to u odjeljku eNV tijekom konfiguracije matrice izgradnje), instaliramo paket pvs-studio. Osim ovoga, naznačeni su i paketi libio-utičnica-ssl-perl и libnet-ssleay-perl, međutim, potrebni su za slanje rezultata poštom, tako da nisu potrebni ako ste odabrali drugu metodu za dostavu izvješća.

Funkcija preuzimanje_ekstrakt preuzima i raspakira navedenu arhivu:

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

Vrijeme je da sastavite projekt. To se događa u odjeljku rukopis:

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 izvorna konfiguracija, osim ovih redaka:

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

U ovom dijelu koda za koji smo postavili cmake zastavica 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 montaža bila uspješna, onda dolazimo nakon_uspjeha, 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 pobliže sljedeće retke:

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 redak generira datoteku licence iz korisničkog imena i ključa koje smo naveli na samom početku prilikom postavljanja varijabli okruženja Travis CI.

Drugi redak izravno započinje analizu. Zastava -j postavlja broj niti za analizu, zastavica -l označava dozvolu, zastavu -o definira datoteku za izlaz dnevnika i zastavu -disableLicenseExpirationCheck potrebno za probne verzije, jer prema zadanim postavkama pvs-studio-analizator upozorit će korisnika da licenca uskoro ističe. Kako biste spriječili da se to dogodi, možete odrediti ovu zastavicu.

Datoteka dnevnika sadrži neobrađeni izlaz koji se ne može čitati bez konverzije, tako da datoteku prvo morate učiniti čitljivom. Provucimo cjepanice plog-pretvarač, a izlaz je html datoteka.

U ovom sam primjeru odlučio poslati izvješća poštom pomoću naredbe Pošalji e-mail.

Kao rezultat, dobili smo sljedeću datoteku .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 gurnete promjene u git repozitorij, nakon čega će Travis CI automatski pokrenuti izgradnju. Kliknite na "ppsspp" za odlazak na izvješća o izradi:

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

Kako konfigurirati PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole
Ako je izgradnja uspješno dovršena, primit ćemo e-poruku s rezultatima statičke analize. Naravno, slanje poštom nije jedini način da dobijete izvješće. Možete odabrati bilo koji način implementacije. Ali važno je zapamtiti da nakon završetka izgradnje neće biti moguće pristupiti datotekama virtualnog stroja.

Sažetak pogreške

Najteži dio smo uspješno završili. Sada se uvjerimo da se sav naš trud isplati. Pogledajmo neke zanimljive točke iz izvješća o statičkoj analizi koje sam dobio poštom (nisam ga uzalud naveo).

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

Upozorenje PVS-Studio: V597 Kompajler bi mogao izbrisati poziv funkcije 'memset', koji se koristi za ispiranje međuspremnika 'sum'. Za brisanje privatnih podataka trebala bi se koristiti funkcija RtlSecureZeroMemory(). sha1.cpp 325

Ovaj dio koda nalazi se u modulu za sigurno raspršivanje, ali sadrži ozbiljan sigurnosni propust (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 uredno i u funkciji memeset se izvršava, čime se prepisuju važni podaci u RAM-u, međutim, nemojte se još radovati. Pogledajmo popis sklopova Release verzije s optimizacijom:

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

Kao što se može vidjeti iz popisa, kompajler je ignorirao poziv memeset. To je zbog činjenice da u funkciji sha1 nakon poziva memeset nema više pozivanja na strukturu ctx. Stoga prevoditelj ne vidi smisao u gubitku procesorskog vremena na prepisivanje memorije koja se u budućnosti ne koristi. To možete popraviti pomoću funkcije RtlSecureZeroMemory ili sličan njoj.

ispraviti:

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

Nepotrebna usporedba

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

Upozorenje PVS-Studio: V547 Izraz 'leftvol >= 0' je uvijek istinit. sceAudio.cpp 120

Prvo obratite pažnju na drugu granu if. Kod će se izvršiti samo ako su ispunjeni svi uvjeti levi vol > 0xFFFF || desni vol > 0xFFFF || lijevovol < 0 || desni vol < 0 će se pokazati lažnim. Stoga dobivamo sljedeće izjave, koje će biti istinite za else granu: levi vol <= 0xFFFF, desni vol <= 0xFFFF, levi vol >= 0 и desni vol >= 0. Obratite pažnju na posljednje dvije izjave. Ima li smisla provjeriti koji je nužan uvjet za izvršenje ovog dijela koda?

Stoga možemo sigurno ukloniti ove uvjetne 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 scenarij. Postoji neka vrsta greške skrivena iza ovih suvišnih uvjeta. Možda nisu provjerili što se traži.

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 pozornost na ček iznutra if. Ne mislite li da je čudno da provjeravamo je li adresa važeća? psmfData, dvostruko više? Tako da mi ovo izgleda čudno... Zapravo, ovo je, naravno, tipfeler, a ideja je bila provjeriti 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");
  }
  ....
}

Upozorenje PVS-Studio: V547 Izraz 'veličina == 8' uvijek je lažan. syn-att.c 195

Ova se pogreška nalazi u mapi ext, tako da nije baš relevantno za projekt, ali je greška pronađena prije nego sam je primijetio, pa sam je odlučio ostaviti. Uostalom, ovaj članak nije o pregledu grešaka, već o integraciji s Travis CI, a konfiguracija analizatora nije provedena.

Promjenjiva Veličina se inicijalizira konstantom, međutim, uopće se ne koristi u kodu, sve do operatora if, što, naravno, daje lažan prilikom provjere uvjeta, jer, kako se sjećamo, Veličina jednaka nuli. Naknadne provjere također nemaju smisla.

Očigledno je autor fragmenta koda zaboravio prebrisati varijablu Veličina prije toga.

zaustaviti

Ovdje ćemo vjerojatno završiti s greškama. Svrha ovog članka je pokazati rad PVS-Studia zajedno s Travis CI, a ne analizirati projekt što je moguće temeljitije. Ako želite veće i ljepše pogreške, uvijek im se možete diviti здесь :).

Zaključak

Korištenje web usluga za izradu projekata zajedno s praksom inkrementalne analize omogućuje vam pronalaženje mnogih problema odmah nakon spajanja koda. Međutim, jedna verzija možda neće biti dovoljna, pa će postavljanje testiranja zajedno sa statičkom analizom značajno poboljšati kvalitetu koda.

korisni linkovi

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

Ako želite podijeliti ovaj članak s publikom koja govori engleski, upotrijebite vezu za prijevod: Maxim Zvyagintsev. Kako postaviti PVS-Studio u Travis CI koristeći primjer emulatora PSP igraće konzole.

Izvor: www.habr.com

Dodajte komentar