Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh
Travis CI adalah layanan web terdistribusi untuk membangun dan menguji perangkat lunak yang menggunakan GitHub sebagai hosting kode sumber. Selain skenario operasi di atas, Anda dapat menambahkan skenario operasi Anda sendiri berkat opsi konfigurasi yang luas. Pada artikel ini kita akan mengkonfigurasi Travis CI untuk bekerja dengan PVS-Studio menggunakan contoh kode PPSSPP.

pengenalan

Travis CI adalah layanan web untuk membangun dan menguji perangkat lunak. Biasanya digunakan bersamaan dengan praktik integrasi berkelanjutan.

PPSSPP β€” Emulator konsol permainan PSP. Program ini mampu meniru peluncuran game apa pun dari image disk yang ditujukan untuk Sony PSP. Program ini dirilis pada 1 November 2012. PPSSPP dilisensikan di bawah GPL v2. Siapa pun dapat melakukan perbaikan kode sumber proyek.

PVS-Studio β€” penganalisis kode statis untuk mencari kesalahan dan potensi kerentanan dalam kode program. Pada artikel ini, sebagai gantinya, kami akan meluncurkan PVS-Studio tidak secara lokal di mesin pengembang, tetapi di cloud, dan mencari kesalahan di PPSSPP.

Menyiapkan Travis CI

Kami memerlukan repositori di GitHub, tempat proyek yang kami perlukan berada, serta kunci untuk PVS-Studio (Anda bisa mendapatkannya kunci percobaan ΠΈΠ»ΠΈ gratis untuk proyek Sumber Terbuka).

Ayo pergi ke situsnya Travis CI. Setelah otorisasi menggunakan akun GitHub Anda, kami akan melihat daftar repositori:

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh
Untuk tesnya saya fork PPSSPP.

Kami mengaktifkan repositori yang ingin kami kumpulkan:

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh
Saat ini, Travis CI tidak dapat membangun proyek kami karena tidak ada instruksi untuk membangunnya. Jadi sudah waktunya untuk konfigurasi.

Selama analisis, beberapa variabel akan berguna bagi kami, misalnya, kunci untuk PVS-Studio, yang tidak diinginkan untuk ditentukan dalam file konfigurasi. Jadi mari tambahkan variabel lingkungan menggunakan pengaturan build di Travis CI:

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh
Kami akan membutuhkan:

  • PVS_USERNAME - nama pengguna
  • PVS_KEY - kunci
  • MAIL_USER - email yang akan digunakan untuk mengirim laporan
  • MAIL_PASSWORD - kata sandi email

Dua yang terakhir adalah opsional. Ini akan digunakan untuk mengirim hasil melalui surat. Jika Anda ingin mendistribusikan laporan dengan cara lain, Anda tidak perlu menentukannya.

Jadi, kami telah menambahkan variabel lingkungan yang kami perlukan:

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh
Sekarang mari kita membuat file .travis.yml dan letakkan di root proyek. PPSSPP sudah memiliki file konfigurasi untuk Travis CI, namun terlalu besar dan sama sekali tidak cocok untuk contoh, jadi kami harus menyederhanakannya dan hanya menyisakan elemen dasar saja.

Pertama, mari tunjukkan bahasanya, versi Ubuntu Linux yang ingin kita gunakan di mesin virtual, dan paket yang diperlukan untuk pembuatannya:

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'

Semua paket yang terdaftar diperlukan khusus untuk PPSSPP.

Sekarang kami menunjukkan matriks perakitan:

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

Sedikit lebih banyak tentang bagian tersebut matriks. Di Travis CI, ada dua cara untuk membuat opsi build: yang pertama adalah menentukan daftar kompiler, tipe sistem operasi, variabel lingkungan, dll., setelah itu matriks dari semua kemungkinan kombinasi dihasilkan; yang kedua adalah indikasi eksplisit dari matriks. Tentu saja, Anda dapat menggabungkan kedua pendekatan ini dan menambahkan kasus unik, atau, sebaliknya, mengecualikannya menggunakan bagian tersebut mengecualikan. Anda dapat membaca lebih lanjut tentang ini di Dokumentasi Travis CI.

Yang tersisa hanyalah memberikan instruksi perakitan khusus proyek:

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 memungkinkan Anda menambahkan perintah Anda sendiri untuk berbagai tahap kehidupan mesin virtual. Bagian sebelum_instal dijalankan sebelum menginstal paket. Kemudian install, yang mengikuti instalasi paket dari daftar addons.aptyang kami sebutkan di atas. Majelis itu sendiri berlangsung di naskah. Jika semuanya berjalan baik, maka kita akan masuk setelah_sukses (di bagian inilah kita akan menjalankan analisis statis). Ini tidak semua langkah yang dapat dimodifikasi, jika Anda membutuhkan lebih banyak, Anda harus memeriksanya Dokumentasi Travis CI.

Untuk kemudahan membaca, perintah ditempatkan dalam skrip terpisah .travis.sh, yang ditempatkan di root proyek.

Jadi kami memiliki file berikut .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

Sebelum menginstal paket, kami akan memperbarui submodulnya. Hal ini diperlukan untuk membangun PPSSPP. Mari tambahkan fungsi pertama ke .travis.sh (perhatikan ekstensinya):

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

Sekarang kita langsung menyiapkan peluncuran otomatis PVS-Studio di Travis CI. Pertama kita perlu menginstal paket PVS-Studio pada 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
}

Di awal fungsi travis_install kami menginstal kompiler yang kami perlukan menggunakan variabel lingkungan. Lalu jika variabelnya $PVS_ANALYZE menyimpan nilai Yes (kami menunjukkannya di bagian env selama konfigurasi matriks build), kami menginstal paket pvs-studio. Selain itu, paket juga ditunjukkan libio-socket-ssl-perl ΠΈ libnet-ssleay-perl, namun, laporan tersebut diperlukan untuk mengirimkan hasil, sehingga tidak diperlukan jika Anda telah memilih metode lain untuk mengirimkan laporan Anda.

Fungsi unduh_ekstrak mengunduh dan membongkar arsip yang ditentukan:

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

Saatnya untuk menyatukan proyek. Hal ini terjadi pada bagian tersebut naskah:

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
}

Sebenarnya, ini adalah konfigurasi asli yang disederhanakan, kecuali untuk baris berikut:

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

Di bagian kode ini kami menetapkannya membuat tandai untuk mengekspor perintah kompilasi. Ini diperlukan untuk penganalisis kode statis. Anda dapat membaca lebih lanjut tentang ini di artikel β€œCara menjalankan PVS-Studio di Linux dan macOS".

Jika perakitan berhasil, maka kita bisa melakukannya setelah_sukses, tempat kami melakukan analisis statis:

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
}

Mari kita lihat lebih dekat baris-baris berikut:

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

Baris pertama menghasilkan file lisensi dari nama pengguna dan kunci yang kami tentukan di awal saat menyiapkan variabel lingkungan Travis CI.

Baris kedua memulai analisis secara langsung. Bendera -J menetapkan jumlah utas untuk analisis, tandai -l menunjukkan lisensi, bendera -Hai mendefinisikan file untuk mengeluarkan log, dan flag -nonaktifkan Pemeriksaan Kedaluwarsa Lisensi diperlukan untuk versi uji coba, karena secara default pvs-studio-analyzer akan memperingatkan pengguna bahwa lisensi akan segera kedaluwarsa. Untuk mencegah hal ini terjadi, Anda dapat menentukan tanda ini.

File log berisi keluaran mentah yang tidak dapat dibaca tanpa konversi, jadi Anda harus membuat file tersebut dapat dibaca terlebih dahulu. Mari kita lewati lognya konverter plog, dan outputnya adalah file html.

Dalam contoh ini, saya memutuskan untuk mengirim laporan melalui surat menggunakan perintah mengirim email.

Hasilnya, kami mendapat file berikut .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;

Sekarang saatnya untuk mendorong perubahan ke repositori git, setelah itu Travis CI akan secara otomatis menjalankan build. Klik β€œppsspp” untuk membuka laporan pembuatan:

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh
Kita akan melihat gambaran umum dari build saat ini:

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh
Jika build berhasil diselesaikan, kami akan menerima email berisi hasil analisis statis. Tentu saja, pengiriman surat bukanlah satu-satunya cara untuk menerima laporan. Anda dapat memilih metode penerapan apa pun. Namun penting untuk diingat bahwa setelah pembangunan selesai, file mesin virtual tidak dapat diakses.

Ringkasan kesalahan

Kami telah berhasil menyelesaikan bagian tersulit. Sekarang mari kita pastikan bahwa semua upaya kita membuahkan hasil. Mari kita lihat beberapa poin menarik dari laporan analisis statis yang datang kepada saya melalui surat (bukan tanpa alasan saya menunjukkannya).

Pengoptimalan yang berbahaya

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

Peringatan PVS-Studio: V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk menghapus buffer 'jumlah'. Fungsi RtlSecureZeroMemory() harus digunakan untuk menghapus data pribadi. sha1.cpp 325

Potongan kode ini terletak di modul hashing aman, namun mengandung kelemahan keamanan yang serius (CWE-14). Mari kita lihat daftar perakitan yang dihasilkan saat mengkompilasi versi Debug:

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

Semuanya teratur dan fungsinya memeset dijalankan, sehingga menimpa data penting dalam RAM, namun jangan bersukacita dulu. Mari kita lihat daftar perakitan versi Rilis dengan optimasi:

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

Seperti yang dapat dilihat dari daftar, kompiler mengabaikan panggilan tersebut memeset. Hal ini disebabkan oleh fakta bahwa dalam fungsinya sha1 setelah panggilan memeset tidak ada lagi referensi ke struktur ctx. Oleh karena itu, kompiler tidak melihat gunanya membuang waktu prosesor untuk menimpa memori yang tidak digunakan di masa mendatang. Anda dapat memperbaikinya dengan menggunakan fungsi tersebut RtlSecureZeroMemory ΠΈΠ»ΠΈ serupa nya.

Dengan benar:

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

Perbandingan yang tidak perlu

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

Peringatan PVS-Studio: V547 Ekspresi 'leftvol >= 0' selalu benar. scceAudio.cpp 120

Perhatikan cabang else untuk yang pertama if. Kode akan dieksekusi hanya jika semua kondisi terpenuhi vol kiri > 0xFFFF || vol kanan > 0xFFFF || vol kiri < 0 || vol kanan < 0 akan berubah menjadi palsu. Oleh karena itu, kita mendapatkan pernyataan berikut, yang berlaku untuk cabang else: vol kiri <= 0xFFFF, vol kanan <= 0xFFFF, vol kiri >= 0 ΠΈ vol kanan >= 0. Perhatikan dua pernyataan terakhir. Apakah masuk akal untuk memeriksa kondisi apa yang diperlukan untuk eksekusi potongan kode ini?

Jadi kita dapat menghapus pernyataan kondisional ini dengan aman:

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

Skenario lain. Ada semacam kesalahan yang tersembunyi di balik kondisi mubazir ini. Mungkin mereka tidak memeriksa apa yang diperlukan.

Ctrl+C Ctrl+V Menyerang Balik

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 Ada sub-ekspresi yang identik '!Memory::IsValidAddress(psmfData)' di kiri dan kanan '||' operator. scePsmf.cpp 703

Perhatikan cek di dalamnya if. Tidakkah menurut Anda aneh jika kami memeriksa apakah alamatnya valid? psmfData, dua kali lebih banyak? Jadi ini terasa aneh bagi saya... Sebenarnya, ini tentu saja salah ketik, dan idenya adalah untuk memeriksa kedua parameter input.

Pilihan yang benar:

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

Variabel yang terlupakan

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

Peringatan PVS-Studio: V547 Ekspresi 'ukuran == 8' selalu salah. syn-att.c 195

Kesalahan ini terletak di folder ext, jadi tidak terlalu relevan dengan proyek tersebut, tetapi bug tersebut ditemukan sebelum saya menyadarinya, jadi saya memutuskan untuk meninggalkannya. Bagaimanapun, artikel ini bukan tentang meninjau kesalahan, tetapi tentang integrasi dengan Travis CI, dan tidak ada konfigurasi penganalisis yang dilakukan.

Variabel ukuran diinisialisasi oleh sebuah konstanta, namun tidak digunakan sama sekali dalam kode, sampai ke operator if, yang tentu saja memberi palsu sambil memeriksa kondisinya, karena seingat kita, ukuran sama dengan nol. Pemeriksaan selanjutnya juga tidak masuk akal.

Rupanya, pembuat potongan kode lupa menimpa variabelnya ukuran sebelum itu.

berhenti

Di sinilah kita mungkin akan berakhir dengan kesalahan. Tujuan artikel ini adalah untuk mendemonstrasikan karya PVS-Studio bersama Travis CI, dan bukan untuk menganalisis proyek tersebut selengkap mungkin. Jika Anda menginginkan kesalahan yang lebih besar dan indah, Anda selalu bisa mengaguminya di sini :).

Kesimpulan

Menggunakan layanan web untuk membangun proyek bersama dengan praktik analisis tambahan memungkinkan Anda menemukan banyak masalah segera setelah menggabungkan kode. Namun, satu build mungkin tidak cukup, jadi menyiapkan pengujian bersama dengan analisis statis akan meningkatkan kualitas kode secara signifikan.

Berguna Link

Cara mengatur PVS-Studio di Travis CI menggunakan emulator PSP sebagai contoh

Jika Anda ingin membagikan artikel ini kepada audiens berbahasa Inggris, silakan gunakan tautan terjemahan: Maxim Zvyagintsev. Cara mengatur PVS-Studio di Travis CI menggunakan contoh emulator konsol game PSP.

Sumber: www.habr.com

Tambah komentar