Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator
Travis CI er en distribueret webservice til at bygge og teste software, der bruger GitHub som kildekodehosting. Ud over ovenstående driftsscenarier kan du tilføje dine egne takket være de omfattende konfigurationsmuligheder. I denne artikel vil vi konfigurere Travis CI til at arbejde med PVS-Studio ved hjælp af PPSSPP-kodeeksemplet.

Indledning

Travis CI er en webservice til at bygge og teste software. Det bruges normalt i forbindelse med kontinuerlig integrationspraksis.

PPSSPP - PSP spillekonsol emulator. Programmet er i stand til at efterligne lanceringen af ​​ethvert spil fra diskbilleder beregnet til Sony PSP. Programmet blev udgivet den 1. november 2012. PPSSPP er licenseret under GPL v2. Alle kan foretage forbedringer projektets kildekode.

PVS Studio — en statisk kodeanalysator til at søge efter fejl og potentielle sårbarheder i programkode. I denne artikel vil vi for en forandring lancere PVS-Studio ikke lokalt på udviklerens maskine, men i skyen og se efter fejl i PPSSPP.

Opsætning af Travis CI

Vi skal bruge et depot på GitHub, hvor det projekt vi skal bruge er placeret, samt en nøgle til PVS-Studio (du kan få prøvenøgle eller gratis til Open Source-projekter).

Lad os gå til siden Travis CI. Efter godkendelse ved hjælp af din GitHub-konto, vil vi se en liste over depoter:

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator
Til testen gaflede jeg PPSSPP.

Vi aktiverer det depot, som vi ønsker at indsamle:

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator
I øjeblikket kan Travis CI ikke bygge vores projekt, fordi der ikke er nogen instruktioner til at bygge. Så det er tid til konfiguration.

Under analysen vil nogle variabler være nyttige for os, for eksempel nøglen til PVS-Studio, som ville være uønsket at specificere i konfigurationsfilen. Så lad os tilføje miljøvariabler ved hjælp af byggeindstillingerne i Travis CI:

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator
Vi har brug for:

  • PVS_USERNAME - brugernavn
  • PVS_KEY - nøgle
  • MAIL_USER - e-mail, der vil blive brugt til at sende rapporten
  • MAIL_PASSWORD - e-mail-adgangskode

De sidste to er valgfrie. Disse vil blive brugt til at sende resultater med posten. Hvis du ønsker at distribuere rapporten på anden måde, behøver du ikke angive dem.

Så vi har tilføjet de miljøvariabler, vi har brug for:

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator
Lad os nu oprette en fil .travis.yml og placere det i roden af ​​projektet. PPSSPP havde allerede en konfigurationsfil til Travis CI, men den var for stor og fuldstændig uegnet til eksemplet, så vi var nødt til at forenkle den kraftigt og kun lade de grundlæggende elementer stå.

Lad os først angive sproget, versionen af ​​Ubuntu Linux, som vi ønsker at bruge i den virtuelle maskine, og de nødvendige pakker til build:

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 pakker, der er anført, er udelukkende nødvendige for PPSSPP.

Nu angiver vi monteringsmatrixen:

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

Lidt mere om afsnittet matrix. I Travis CI er der to måder at oprette byggemuligheder på: Den første er at specificere en liste over compilere, operativsystemtyper, miljøvariabler osv., hvorefter en matrix med alle mulige kombinationer genereres; den anden er en eksplicit indikation af matrixen. Selvfølgelig kan du kombinere disse to tilgange og tilføje et unikt tilfælde, eller tværtimod udelukke det ved hjælp af afsnittet udelukke. Det kan du læse mere om i Travis CI dokumentation.

Tilbage er blot at give projektspecifikke monteringsvejledninger:

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 giver dig mulighed for at tilføje dine egne kommandoer til forskellige stadier af livet på en virtuel maskine. Afsnit før_installer udført før installation af pakker. Derefter installere, som følger installationen af ​​pakker fra listen addons.aptsom vi har nævnt ovenfor. Selve montagen foregår i script. Hvis alt gik godt, så befinder vi os i efter_succes (det er i dette afsnit, vi vil køre statisk analyse). Det er ikke alle de trin, der kan ændres, hvis du har brug for flere, så bør du kigge ind Travis CI dokumentation.

For at lette læsningen blev kommandoerne placeret i et separat script .travis.sh, som er placeret ved projektroden.

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 pakkerne, opdaterer vi undermodulerne. Dette er nødvendigt for at bygge PPSSPP. Lad os tilføje den første funktion til .travis.sh (bemærk udvidelsen):

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

Nu kommer vi direkte til at opsætte den automatiske lancering af PVS-Studio i Travis CI. Først skal 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 begyndelsen af ​​funktionen travis_install vi installerer de compilere, vi har brug for, ved hjælp af miljøvariabler. Så hvis variablen $PVS_ANALYZE gemmer værdi Ja (vi angav det i afsnittet env under build-matrix-konfiguration), installerer vi pakken pvs-studie. Udover dette er pakker også angivet libio-socket-ssl-perl и libnet-ssleay-perl, de er dog nødvendige for at sende resultater, så de er ikke nødvendige, hvis du har valgt en anden metode til at levere din rapport.

Funktion download_extract downloader og pakker det angivne arkiv ud:

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

Det er tid til at sætte projektet sammen. Dette sker i afsnittet 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 konfiguration, bortset fra disse linjer:

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

I denne sektion af kode sætter vi for cmake flag til eksport af kompileringskommandoer. Dette er nødvendigt for en statisk kodeanalysator. Det kan du læse mere om i artiklen "Sådan kører du PVS-Studio på Linux og macOS".

Hvis samlingen lykkedes, så kommer vi til efter_succes, hvor vi udfø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
}

Lad os 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 linje genererer en licensfil fra brugernavnet og nøglen, som vi specificerede helt i begyndelsen, da vi satte Travis CI-miljøvariablerne op.

Den anden linje starter analysen direkte. Flag -j indstiller antallet af tråde til analyse, flag -l angiver licens, flag -o definerer filen til udlæsning af logfiler og flaget -disableLicenseExpirationCheck påkrævet for prøveversioner, da som standard pvs-studio-analyzer vil advare brugeren om, at licensen er ved at udløbe. For at forhindre dette i at ske, kan du angive dette flag.

Logfilen indeholder rå output, der ikke kan læses uden konvertering, så du skal først gøre filen læsbar. Lad os sende loggene igennem plog-konverter, og outputtet er en html-fil.

I dette eksempel besluttede jeg at sende rapporter med post ved hjælp af kommandoen Send e-mail.

Som et resultat fik 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;

Nu er det tid til at skubbe ændringerne til git-lageret, hvorefter Travis CI automatisk vil køre buildet. Klik på "ppsspp" for at gå til byggerapporterne:

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator
Vi vil se en oversigt over den nuværende bygning:

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator
Hvis opbygningen er fuldført, modtager vi en e-mail med resultaterne af den statiske analyse. Selvfølgelig er mailing ikke den eneste måde at modtage en rapport på. Du kan vælge enhver implementeringsmetode. Men det er vigtigt at huske, at efter at build er afsluttet, vil det ikke være muligt at få adgang til de virtuelle maskinfiler.

Fejloversigt

Vi har med succes afsluttet den sværeste del. Lad os nu sikre os, at alle vores anstrengelser er det værd. Lad os se på nogle interessante punkter fra den statiske analyserapport, der kom til mig med posten (det var ikke for ingenting, jeg 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 advarsel: V597 Compileren kunne slette 'memset' funktionskaldet, som bruges til at tømme 'sum' buffer. Funktionen RtlSecureZeroMemory() skal bruges til at slette de private data. sha1.cpp 325

Dette stykke kode er placeret i det sikre hashing-modul, men det indeholder en alvorlig sikkerhedsfejl (CWE-14). Lad os se på samlingslisten, der genereres ved kompilering af Debug-versionen:

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

Alt er i orden og funktion memeset udføres, og derved overskrive vigtige data i RAM, glæd dig dog ikke lige foreløbig. Lad os se på samlingslisten for udgivelsesversionen med optimering:

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

Som det fremgår af listen, ignorerede compileren opkaldet memeset. Dette skyldes, at der i funktionen sha1 efter opkaldet memeset ikke længere reference til struktur ctx. Derfor ser compileren ingen mening i at spilde processortid på at overskrive hukommelse, der ikke bruges i fremtiden. Du kan rette dette ved at bruge funktionen RtlSecureZeroMemory eller en lignende til hende.

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 Udtrykket 'venstrevol >= 0' er altid sandt. sceAudio.cpp 120

Vær opmærksom på den anden gren for den første if. Koden vil kun blive udført, hvis alle betingelser leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || højrevol < 0 vil vise sig at være falsk. Derfor får vi følgende udsagn, som vil være sande for den anden gren: leftvol <= 0xFFFF, rightvol <= 0xFFFF, venstrevol. >= 0 и højrevolum >= 0. Læg mærke til de to sidste udsagn. Giver det mening at tjekke, hvad der er en nødvendig betingelse for at udføre dette stykke kode?

Så vi kan roligt fjerne disse betingede erklæringer:

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 andet scenarie. Der er en form for fejl gemt bag disse redundante forhold. Måske tjekkede de ikke, hvad der krævedes.

Ctrl+C Ctrl+V slår tilbage

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

Vær opmærksom på checken indeni if. Synes du ikke, det er mærkeligt, at vi tjekker, om adressen er gyldig? psmfData, dobbelt så meget? Så det virker mærkeligt for mig... Faktisk er dette selvfølgelig en tastefejl, og tanken var at tjekke begge inputparametre.

Korrekt mulighed:

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 Udtrykket 'størrelse == 8' er altid falsk. syn-att.c 195

Denne fejl er placeret i mappen ext, så ikke rigtig relevant for projektet, men fejlen blev fundet før jeg lagde mærke til den, så jeg besluttede at forlade den. Denne artikel handler jo ikke om gennemgang af fejl, men om integration med Travis CI, og der blev ikke foretaget nogen konfiguration af analysatoren.

Variabel størrelse initialiseres af en konstant, men den bruges slet ikke i koden, helt ned til operatøren if, hvilket selvfølgelig giver falsk mens vi tjekker forholdene, fordi, som vi husker, størrelse lig med nul. Efterfølgende kontroller giver heller ingen mening.

Tilsyneladende glemte forfatteren af ​​kodefragmentet at overskrive variablen størrelse inden da.

Stands

Det er her, vi nok ender med fejlene. Formålet med denne artikel er at demonstrere arbejdet i PVS-Studio sammen med Travis CI, og ikke at analysere projektet så grundigt som muligt. Hvis du vil have større og smukkere fejl, kan du altid beundre dem her :).

Konklusion

Brug af webtjenester til at bygge projekter sammen med praksis med trinvis analyse giver dig mulighed for at finde mange problemer umiddelbart efter sammenlægning af kode. Men én build er muligvis ikke nok, så opsætning af test sammen med statisk analyse vil forbedre kvaliteten af ​​koden markant.

Nyttige links

Sådan konfigureres PVS-Studio i Travis CI ved at bruge eksemplet med en PSP-spillekonsollemulator

Hvis du vil dele denne artikel med et engelsktalende publikum, så brug venligst oversættelseslinket: Maxim Zvyagintsev. Sådan opsætter du PVS-Studio i Travis CI ved at bruge eksemplet med PSP-spillekonsollemulatoren.

Kilde: www.habr.com

Tilføj en kommentar