Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator
Travis CI er en distribuert webtjeneste for å bygge og teste programvare som bruker GitHub som hosting for kildekode. I tillegg til de ovennevnte driftsscenariene, kan du legge til dine egne takket være de omfattende konfigurasjonsalternativene. I denne artikkelen vil vi konfigurere Travis CI til å fungere med PVS-Studio ved å bruke PPSSPP-kodeeksemplet.

Innledning

Travis C.I. er en webtjeneste for å bygge og teste programvare. Det brukes vanligvis i forbindelse med kontinuerlig integreringspraksis.

PPSSPP - PSP-spillkonsoll-emulator. Programmet er i stand til å emulere lanseringen av alle spill fra diskbilder beregnet for Sony PSP. Programmet ble utgitt 1. november 2012. PPSSPP er lisensiert under GPL v2. Hvem som helst kan gjøre forbedringer på prosjektets kildekode.

PVS-Studio — en statisk kodeanalysator for å søke etter feil og potensielle sårbarheter i programkode. I denne artikkelen vil vi for en endring starte PVS-Studio ikke lokalt på utviklerens maskin, men i skyen, og se etter feil i PPSSPP.

Sette opp Travis CI

Vi trenger et depot på GitHub, hvor prosjektet vi trenger er lokalisert, samt en nøkkel for PVS-Studio (du kan få prøvenøkkel eller gratis for åpen kildekode-prosjekter).

La oss gå til siden Travis C.I.. Etter autorisasjon ved å bruke GitHub-kontoen din, vil vi se en liste over depoter:

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator
For testen gaffel jeg PPSSPP.

Vi aktiverer depotet som vi ønsker å samle inn:

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator
For øyeblikket kan ikke Travis CI bygge prosjektet vårt fordi det ikke er instruksjoner for bygging. Så det er tid for konfigurasjon.

Under analysen vil noen variabler være nyttige for oss, for eksempel nøkkelen til PVS-Studio, som ville være uønsket å spesifisere i konfigurasjonsfilen. Så la oss legge til miljøvariabler ved å bruke byggeinnstillingene i Travis CI:

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator
Vi trenger:

  • PVS_USERNAME - brukernavn
  • PVS_KEY - nøkkel
  • MAIL_USER - e-post som vil bli brukt til å sende rapporten
  • MAIL_PASSWORD - e-postpassord

De to siste er valgfrie. Disse vil bli brukt til å sende resultater per post. Dersom du ønsker å distribuere rapporten på annen måte, trenger du ikke angi dem.

Så vi har lagt til miljøvariablene vi trenger:

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator
La oss nå lage en fil .travis.yml og plassere den i roten av prosjektet. PPSSPP hadde allerede en konfigurasjonsfil for Travis CI, men den var for stor og helt uegnet for eksempelet, så vi måtte forenkle den betraktelig og la bare de grunnleggende elementene være igjen.

La oss først angi språket, versjonen av Ubuntu Linux som vi ønsker å bruke i den virtuelle maskinen, og de nødvendige pakkene for byggingen:

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'

Alle pakkene som er oppført er utelukkende nødvendig for PPSSPP.

Nå indikerer 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

Litt mer om avsnittet matrise. I Travis CI er det to måter å lage byggealternativer på: den første er å spesifisere en liste over kompilatorer, operativsystemtyper, miljøvariabler osv., hvoretter en matrise med alle mulige kombinasjoner genereres; den andre er en eksplisitt indikasjon på matrisen. Selvfølgelig kan du kombinere disse to tilnærmingene og legge til en unik sak, eller tvert imot, ekskludere den ved å bruke delen ekskludere. Du kan lese mer om dette i Travis CI-dokumentasjon.

Alt som gjenstår er å gi prosjektspesifikke monteringsinstruksjoner:

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 lar deg legge til dine egne kommandoer for ulike stadier av livet til en virtuell maskin. Seksjon før_installer utført før du installerer pakker. Deretter installere, som følger installasjonen av pakker fra listen addons.aptsom vi indikerte ovenfor. Selve monteringen foregår i script. Hvis alt gikk bra, så befinner vi oss i etter_suksess (det er i denne delen vi skal kjøre statisk analyse). Dette er ikke alle trinnene som kan endres, hvis du trenger flere, bør du se inn Travis CI-dokumentasjon.

For å lette lesingen ble kommandoene plassert i et eget skript .travis.sh, som er plassert ved prosjektroten.

Så vi har følgende 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

Før vi installerer pakkene, vil vi oppdatere undermodulene. Dette er nødvendig for å bygge PPSSPP. La oss legge til den første funksjonen til .travis.sh (merk utvidelsen):

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

Nå kommer vi direkte til å sette opp den automatiske lanseringen av PVS-Studio i Travis CI. Først må vi installere PVS-Studio-pakken 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 begynnelsen av funksjonen travis_install vi installerer kompilatorene vi trenger ved å bruke miljøvariabler. Så hvis variabelen $PVS_ANALYZE lagrer verdi Ja (vi indikerte det i delen env under byggematrisekonfigurasjon), installerer vi pakken pvs-studio. I tillegg til dette er det også angitt pakker libio-socket-ssl-perl и libnet-ssleay-perl, men de kreves for å sende resultater, så de er ikke nødvendige hvis du har valgt en annen metode for å levere rapporten.

Funksjon download_extract laster ned og pakker ut det angitte arkivet:

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

Det er på tide å sette sammen prosjektet. Dette skjer i seksjonen 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
}

Faktisk er dette en forenklet original konfigurasjon, bortsett fra disse linjene:

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

I denne delen av koden setter vi for cmake flagg for eksport av kompileringskommandoer. Dette er nødvendig for en statisk kodeanalysator. Du kan lese mer om dette i artikkelen "Hvordan kjøre PVS-Studio på Linux og macOS".

Hvis monteringen var vellykket, så kommer vi til etter_suksess, hvor vi utfører statisk analyse:

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
}

La oss se nærmere på følgende linjer:

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ørste linjen genererer en lisensfil fra brukernavnet og nøkkelen som vi spesifiserte helt i begynnelsen da vi satte opp Travis CI-miljøvariablene.

Den andre linjen starter analysen direkte. Flagg -j setter antall tråder for analyse, flagg -l indikerer lisens, flagg -o definerer filen for å skrive ut logger, og flagget -disableLicenseExpirationCheck kreves for prøveversjoner, siden som standard pvs-studio-analyzer vil advare brukeren om at lisensen er i ferd med å utløpe. For å forhindre at dette skjer, kan du spesifisere dette flagget.

Loggfilen inneholder råutdata som ikke kan leses uten konvertering, så du må først gjøre filen lesbar. La oss føre tømmerstokkene plog-omformer, og utdataene er en html-fil.

I dette eksemplet bestemte jeg meg for å sende rapporter per post ved å bruke kommandoen Send e-post.

Som et resultat fikk vi følgende 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;

Nå er det på tide å presse endringene til git-depotet, hvoretter Travis CI automatisk kjører byggingen. Klikk på "ppsspp" for å gå til byggerapportene:

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator
Vi vil se en oversikt over den nåværende konstruksjonen:

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator
Hvis byggingen er fullført, vil vi motta en e-post med resultatene av den statiske analysen. Sending er selvfølgelig ikke den eneste måten å motta en rapport på. Du kan velge hvilken som helst implementeringsmetode. Men det er viktig å huske at etter at byggingen er fullført, vil det ikke være mulig å få tilgang til de virtuelle maskinfilene.

Feilsammendrag

Vi har fullført den vanskeligste delen. La oss nå sørge for at all vår innsats er verdt det. La oss se på noen interessante punkter fra den statiske analyserapporten som kom til meg per post (det var ikke for ingenting jeg indikerte det).

Farlig optimalisering

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 advarsel: V597 Kompilatoren kan slette 'memset'-funksjonskallet, som brukes til å tømme 'sum'-buffer. Funksjonen RtlSecureZeroMemory() skal brukes til å slette de private dataene. sha1.cpp 325

Denne kodebiten er plassert i den sikre hashing-modulen, men den inneholder en alvorlig sikkerhetsfeil (CWE-14). La oss se på monteringslisten som genereres når du kompilerer Debug-versjonen:

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

Alt er i orden og funksjonen memeset blir utført, og overskriver dermed viktige data i RAM, men gled deg ikke ennå. La oss se på monteringslisten til utgivelsesversjonen med optimalisering:

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

Som det fremgår av oppføringen, ignorerte kompilatoren samtalen memeset. Dette skyldes det faktum at i funksjonen sha1 etter samtalen memeset ingen mer henvisning til struktur ctx. Derfor ser kompilatoren ikke noe poeng i å kaste bort prosessortid på å overskrive minne som ikke brukes i fremtiden. Du kan fikse dette ved å bruke funksjonen RtlSecureZeroMemory eller en lignende til henne.

korrigere:

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

Unødvendig sammenligning

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 advarsel: V547 Uttrykket 'venstrevol >= 0' er alltid sant. sceAudio.cpp 120

Vær oppmerksom på den andre grenen for den første if. Koden vil kun utføres under alle forhold leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || høyrevolum < 0 vil vise seg å være falsk. Derfor får vi følgende utsagn, som vil være sant for den andre grenen: leftvol <= 0xFFFF, rightvol <= 0xFFFF, venstrevolum >= 0 и høyrevolum >= 0. Legg merke til de to siste utsagnene. Er det fornuftig å sjekke hva som er en nødvendig betingelse for utførelse av denne kodebiten?

Så vi kan trygt fjerne disse betingede uttalelsene:

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

Et annet scenario. Det er en slags feil skjult bak disse redundante forholdene. Kanskje de ikke sjekket hva som kreves.

Ctrl+C Ctrl+V slår tilbake

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 er identiske underuttrykk '!Memory::IsValidAddress(psmfData)' til venstre og til høyre for '||' operatør. scePsmf.cpp 703

Vær oppmerksom på sjekken inni if. Synes du ikke det er rart at vi sjekker om adressen er gyldig? psmfData, Dobbelt så mye? Så dette virker rart for meg... Faktisk er dette selvfølgelig en skrivefeil, og tanken var å sjekke begge inngangsparametrene.

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

Glemt 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 advarsel: V547 Uttrykket 'størrelse == 8' er alltid usant. syn-att.c 195

Denne feilen ligger i mappen ext, så egentlig ikke relevant for prosjektet, men feilen ble funnet før jeg la merke til den, så jeg bestemte meg for å forlate den. Denne artikkelen handler tross alt ikke om gjennomgang av feil, men om integrasjon med Travis CI, og det ble ikke utført noen konfigurasjon av analysatoren.

variabel størrelse initialiseres av en konstant, men den brukes ikke i det hele tatt i koden, helt ned til operatøren if, som selvfølgelig gir falsk mens vi sjekker forholdene, fordi, som vi husker, størrelse lik null. Etterfølgende kontroller gir heller ingen mening.

Tilsynelatende glemte forfatteren av kodefragmentet å overskrive variabelen størrelse før det.

Stopp

Det er her vi sannsynligvis ender med feilene. Hensikten med denne artikkelen er å demonstrere arbeidet til PVS-Studio sammen med Travis CI, og ikke å analysere prosjektet så grundig som mulig. Hvis du vil ha større og vakrere feil, kan du alltid beundre dem her :).

Konklusjon

Ved å bruke webtjenester til å bygge prosjekter sammen med praksisen med inkrementell analyse kan du finne mange problemer umiddelbart etter sammenslåing av kode. Det er imidlertid ikke sikkert at ett bygg er nok, så å sette opp testing sammen med statisk analyse vil forbedre kvaliteten på koden betydelig.

Nyttige lenker

Hvordan konfigurere PVS-Studio i Travis CI ved å bruke eksempelet på en PSP-spillkonsoll-emulator

Hvis du vil dele denne artikkelen med et engelsktalende publikum, vennligst bruk oversettelseslenken: Maxim Zvyagintsev. Hvordan sette opp PVS-Studio i Travis CI ved å bruke eksemplet med PSP-spillkonsoll-emulator.

Kilde: www.habr.com

Legg til en kommentar