Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP
Travis CI hè un serviziu web distribuitu per custruisce è teste di software chì usa GitHub cum'è hosting di codice fonte. In più di i scenari operativi sopra, pudete aghjunghje u vostru propiu grazia à l'opzioni di cunfigurazione estensive. In questu articulu, cunfiguremu Travis CI per travaglià cù PVS-Studio cù l'esempiu di codice PPSSPP.

Introduzione

Travis C.I. hè un serviziu web per custruisce è pruvà software. Hè di solitu usatu in cunjunzione cù pratiche di integrazione cuntinuu.

PPSSPP - Emulatore di cunsola di ghjocu PSP. U prugramma hè capaci di emulà u lanciu di qualsiasi ghjoculi da l'imaghjini di discu destinati à Sony PSP. U prugramma hè stata liberata l'1 di nuvembre di u 2012. PPSSPP hè licenziatu sottu GPL v2. Qualchissia pò fà migliurà codice fonte di u prughjettu.

PVS Studio - un analizzatore di codice staticu per a ricerca di l'errori è e vulnerabilità potenziali in u codice di u prugramma. In questu articulu, per un cambiamentu, lanciaremu PVS-Studio micca in u locu nantu à a macchina di u sviluppatore, ma in u nuvulu, è cercate l'errori in PPSSPP.

Mise en place de Travis CI

Avemu bisognu di un repository in GitHub, induve u prughjettu chì avemu bisognu hè situatu, è ancu una chjave per PVS-Studio (pudete uttene chjave di prova o gratuitu per i prughjetti Open Source).

Andemu à u situ Travis C.I.. Dopu l'autorizazione cù u vostru contu GitHub, vedemu una lista di repositori:

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP
Per a prova, aghju forked PPSSPP.

Attivemu u repository chì vulemu cullà:

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP
À u mumentu, Travis CI ùn pò micca custruisce u nostru prughjettu perchè ùn ci hè micca struzzioni per a custruzione. Allora hè u tempu di cunfigurazione.

Duranti l'analisi, certi variàbili seranu utili per noi, per esempiu, a chjave per PVS-Studio, chì ùn saria micca desideratu di specificà in u schedariu di cunfigurazione. Allora aghjustemu variabili di l'ambiente utilizendu i paràmetri di creazione in Travis CI:

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP
Averemu bisognu:

  • PVS_USERNAME - nome d'utilizatore
  • PVS_KEY - chjave
  • MAIL_USER - email chì serà utilizatu per mandà u rapportu
  • MAIL_PASSWORD - password email

L'ultimi dui sò opzionali. Quessi seranu usati per mandà risultati per mail. Se vulete distribuisce u rapportu in un altru modu, ùn avete micca bisognu di indicà.

Dunque, avemu aghjustatu e variabili di l'ambiente chì avemu bisognu:

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP
Avà criemu un schedariu .travis.yml è mette in a radica di u prugettu. PPSSPP hà digià avutu un schedariu di cunfigurazione per Travis CI, in ogni modu, era troppu grande è completamente inadatta per l'esempiu, cusì avemu avutu a simplificà assai è lascià solu l'elementi basi.

Prima, indichemu a lingua, a versione di Ubuntu Linux chì vulemu usà in a macchina virtuale, è i pacchetti necessarii per a custruzione:

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'

Tutti i pacchetti chì sò listati sò necessarii solu per PPSSPP.

Avà indichemu a matrice di assemblea:

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

Un pocu più nantu à a rùbbrica Matrici. In Travis CI, ci sò duie manere di creà opzioni di custruzzione: u primu hè di specificà una lista di compilatori, tipi di sistema operatore, variabili di l'ambienti, etc., dopu chì una matrice di tutte e cumminazzioni pussibuli hè generata; u sicondu hè una indicazione esplicita di a matrice. Di sicuru, pudete cunghjuntà sti dui approcci è aghjunghje un casu unicu, o, à u cuntrariu, escludillu cù a sezione escludiri. Pudete leghje più nantu à questu in Documentation Travis CI.

Il ne reste plus qu'à fournir des instructions d'assemblage spécifiques au projet :

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 permette di aghjunghje i vostri cumandamenti per diverse tappe di a vita di una macchina virtuale. Sezzione prima_installà eseguitu prima di installà i pacchetti. Allora stallà, chì seguita a stallazione di pacchetti da a lista addons.aptchì avemu indicatu sopra. L'assemblea stessa si svolge in scrittura. Sè tuttu andava bè, allora ci ritruvemu dopu_successu (hè in questa sezione chì eseguiremu l'analisi statica). Quessi ùn sò micca tutti i passi chì ponu esse mudificate, se avete bisognu di più, allora duvete circà Documentation Travis CI.

Per facilità di lettura, i cumandamenti sò stati posti in un script separatu .travis.sh, chì si trova à a radica di u prugettu.

Allora avemu u schedariu seguente .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

Prima di stallà i pacchetti, aghjurneremu i submoduli. Questu hè necessariu per custruisce PPSSPP. Aghjunghjemu a prima funzione .travis.sh (nota l'estensione):

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

Avà venenu direttamente à stallà u lanciu automaticu di PVS-Studio in Travis CI. Prima avemu bisognu di stallà u pacchettu PVS-Studio in u sistema:

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
}

À u principiu di a funzione travis_install installemu i compilatori chì avemu bisognu cù variabili di l'ambiente. Allora se a variàbile $PVS_ANALYZE conserva u valore (l'avemu indicatu in a sezione env durante a cunfigurazione di a matrice di creazione), installemu u pacchettu pvs-studio. In più di questu, i pacchetti sò ancu indicati libio-socket-ssl-perl и libnet-ssleay-perl, in ogni casu, sò richiesti per i risultati di mailing, perchè ùn sò micca necessarii se avete sceltu un altru metudu per trasmette u vostru rapportu.

funziunava download_extract scarica è decomprime l'archiviu specificatu:

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

Hè ora di mette u prugettu inseme. Questu succede in a sezione scrittura:

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
}

In fatti, questa hè una cunfigurazione originale simplificata, eccettu per queste linee:

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

In questa sezione di codice avemu stabilitu cmake bandiera per l'esportazione di cumandamenti di compilazione. Questu hè necessariu per un analizzatore di codice staticu. Pudete leghje più nantu à questu in l'articulu "Cumu eseguisce PVS-Studio in Linux è macOS".

Se l'assemblea hè stata successu, allora ghjunghjemu dopu_successu, induve realicemu analisi statica:

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
}

Fighjemu più attente à e seguenti linee:

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

A prima linea genera un schedariu di licenza da u nome d'utilizatore è a chjave chì avemu specificatu à u principiu quandu cunfigurà e variabili di l'ambiente Travis CI.

A seconda linea principia l'analisi direttamente. Bandiera -j stabilisce u numeru di fili per l'analisi, bandiera -l indica licenza, bandiera -o definisce u schedariu per l'output logs, è a bandiera -disableLicenseExpirationCheck necessariu per e versioni di prova, postu chì per difettu pvs-studio-analyzer avvisarà l'utilizatore chì a licenza hè per scade. Per impediscenu chì questu succede, pudete specificà sta bandiera.

U schedariu di logu cuntene un output crudu chì ùn pò micca esse lettu senza cunversione, per quessa, deve prima rende u schedariu leggibile. Passemu i logs plog-convertitore, è l'output hè un schedariu html.

In questu esempiu, aghju decisu di mandà rapporti per mail cù u cumandamentu mandà un mail.

In u risultatu, avemu avutu u schedariu seguente .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;

Avà hè u tempu di spinghje i cambiamenti à u repository git, dopu chì Travis CI eseguirà automaticamente a custruzione. Cliccate nantu à "ppsspp" per andà à i rapporti di creazione:

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP
Videremu una panoramica di a custruzione attuale:

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP
Se a custruzione hè cumpleta cù successu, riceveremu un email cù i risultati di l'analisi statica. Di sicuru, u mailing ùn hè micca l'unicu modu per riceve un rapportu. Pudete sceglie qualsiasi metudu di implementazione. Ma hè impurtante di ricurdà chì dopu chì a custruzione hè finita, ùn serà micca pussibule di accede à i schedarii di a macchina virtuale.

Riassuntu di errore

Avemu cumpletu cù successu a parte più difficiule. Avà assicuremu chì tutti i nostri sforzi valenu a pena. Fighjemu qualchi punti interessanti da u rapportu di analisi statica chì m'hè ghjuntu per mail (ùn era per nunda chì l'aghju indicatu).

Ottimisazione periculosa

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

Avvisu di PVS-Studio: V597 U compilatore puderia sguassate a chjama di a funzione "memset", chì hè aduprata per sguassà u buffer "sum". A funzione RtlSecureZeroMemory() deve esse usata per sguassà i dati privati. sha1.cpp 325

Stu pezzu di codice hè situatu in u modulu di hashing sicuru, ma cuntene un difettu di sicurità seriu (CWE-14). Fighjemu a lista di l'assemblea chì hè generata quandu compilate a versione Debug:

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

Tuttu hè in ordine è a funzione memeset hè eseguitu, soprascrivite cusì dati impurtanti in RAM, però, ùn vi rallegrate micca. Fighjemu a lista di l'assemblea di a versione Release cù ottimisazione:

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

Comu pò esse vistu da a lista, u compilatore ignorava a chjama memeset. Questu hè duvuta à u fattu chì in a funzione sha1 dopu à a chjama memeset ùn ci hè più riferimentu à a struttura ctx. Per quessa, u compilatore ùn vede nunda di perda u tempu di u processatore soprascrive a memoria chì ùn hè micca utilizata in u futuru. Pudete riparà questu utilizendu a funzione RtlSecureZeroMemory o simili à ella.

Correfica:

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

Paragone inutile

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

Avvisu di PVS-Studio: V547 L'espressione "leftvol >= 0" hè sempre vera. sceAudio.cpp 120

Prestate attenzione à l'altru ramu per u primu if. U codice serà eseguitu solu se tutte e cundizioni leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0 risulterà esse falsu. Dunque, avemu i seguenti dichjarazioni, chì serà vera per l'altru ramu: leftvol <= 0xFFFF, rightvol <= 0xFFFF, leftvol >= 0 и rightvol >= 0. Nota l'ultimi dui dichjarazioni. Hè sensu per verificà quale hè una cundizione necessaria per l'esekzione di stu pezzu di codice?

Allora pudemu sguassà in modu sicuru sti dichjarazioni cundiziunali:

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

Un altru scenariu. Ci hè qualchì tipu d'errore ammucciatu daretu à sti cundizioni redundante. Forse ùn anu micca verificatu ciò chì era necessariu.

Ctrl+C Ctrl+V colpisce

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 Ci sò sub-espressioni idèntica '!Memory::IsValidAddress(psmfData)' à a manca è à a diritta di u '||' operatore. scePsmf.cpp 703

Prestate attenzione à u cuntrollu di l'internu if. Ùn vi pare micca stranu chì avemu verificatu se l'indirizzu hè validu ? psmfData, duie volte più ? Allora questu mi pare stranu... In fatti, questu hè, sicuru, un typo, è l'idea era di verificà i dui paràmetri di input.

Opzione curretta:

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

Variabile scurdata

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

Avvisu di PVS-Studio: V547 L'espressione 'taglia == 8' hè sempre falsa. syn-att.c 195

Stu errore hè situatu in u cartulare ext, cusì micca veramente pertinenti à u prugettu, ma u bug hè statu truvatu prima di avè nutatu, cusì decisu di lascià. Dopu tuttu, questu articulu ùn hè micca di rivisione di l'errori, ma di integrazione cù Travis CI, è nisuna cunfigurazione di l'analizzatore hè stata realizata.

Variable grannizza hè inizializatu da una constante, però, ùn hè micca usatu in tuttu in u codice, ghjustu finu à l'operatore if, chì, sicuru, dà sbagliate mentre cuntrollanu e cundizioni, perchè, cum'è ricurdemu, grannizza uguali à zero. I cuntrolli successivi ùn anu ancu sensu.

Apparentemente, l'autore di u frammentu di codice s'hè scurdatu di overwrite a variàbile grannizza prima di quessa.

Stop

Hè quì chì probabilmente finiremu cù i sbagli. U scopu di stu articulu hè di dimustrà u travagliu di PVS-Studio inseme cù Travis CI, è micca di analizà u prugettu cum'è pussibili. Se vulete sbaglii più grande è più belli, pudete sempre ammirallu ccà :).

cunchiusioni

Utilizà i servizii web per custruisce prughjetti inseme cù a pratica di l'analisi incrementali permette di truvà parechji prublemi immediatamenti dopu a fusione di codice. Tuttavia, una custruzzione pò esse micca abbastanza, cusì a creazione di teste cù l'analisi statica migliurà significativamente a qualità di u codice.

E ligami utili

Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di un emulatore di console di ghjocu PSP

Se vulete sparte stu articulu cù un publicu anglofonu, per piacè utilizate u ligame di traduzzione: Maxim Zvyagintsev. Cumu cunfigurà PVS-Studio in Travis CI utilizendu l'esempiu di l'emulatore di console di ghjocu PSP.

Source: www.habr.com

Add a comment