Bagaimana mengkonfigurasi PVS-Studio di Travis CI menggunakan emulator konsol game PSP sebagai contoh

PPSSPP

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

Pendahuluan


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

PPSSPP - emulator konsol game PSP. Program ini mampu meniru peluncuran game apa pun dari gambar disk yang dirancang untuk Sony PSP. Program ini dirilis pada 1 November 2012. PPSSPP dilisensikan di bawah GPL v2. Siapa pun dapat melakukan perbaikan pada kode sumber proyek .

PVS-Studio adalah penganalisa kode statis untuk mencari kesalahan dan kerentanan potensial dalam kode program. Dalam artikel ini, untuk perubahan, kami akan meluncurkan PVS-Studio tidak secara lokal di mesin pengembang, tetapi di cloud, dan mencari kesalahan di PPSSPP.

Konfigurasikan Travis CI


Kami akan membutuhkan repositori di GitHub, di mana proyek yang kami butuhkan terletak, serta kunci untuk PVS-Studio (Anda bisa mendapatkan kunci percobaan atau gratis untuk proyek-proyek Open Source ).

Mari kita pergi ke situs web Travis CI . Setelah otorisasi menggunakan akun GitHub, kami akan memiliki daftar repositori:


Untuk tes, saya bercabang PPSSPP.

Kami mengaktifkan repositori yang ingin kami kumpulkan:


Saat ini, Travis CI tidak dapat mengumpulkan proyek kami, karena tidak ada instruksi untuk perakitan. Oleh karena itu, ini saatnya konfigurasi.

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


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. Mereka akan digunakan untuk mengirim hasilnya melalui surat. Jika Anda ingin mengirim laporan dengan cara lain, maka Anda tidak perlu menentukannya.

Jadi, kami menambahkan variabel lingkungan yang kami butuhkan:


Sekarang buat file .travis.yml dan letakkan di root proyek. File konfigurasi untuk Travis CI sudah ada di PPSSPP, namun, itu terlalu besar dan sama sekali tidak cocok untuk contoh, jadi saya harus menyederhanakannya secara signifikan dan hanya menyisakan elemen dasar.

Pertama, kami menunjukkan bahasa, versi Ubuntu Linux yang ingin kami gunakan di mesin virtual, dan paket yang diperlukan untuk perakitan:

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 hanya untuk PPSSPP.

Sekarang tentukan matriks rakitan:

 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 matriks . Dalam Travis CI, ada dua cara untuk membuat opsi build: yang pertama adalah membuat daftar kompiler, jenis sistem operasi, variabel lingkungan, dll., Setelah itu, matriks semua kombinasi yang mungkin dihasilkan; yang kedua adalah indikasi eksplisit dari matriks. Tentu saja, Anda dapat menggabungkan kedua pendekatan ini dan menambahkan kasus unik, atau, sebagai alternatif, mengecualikan menggunakan bagian mengecualikan . Baca lebih lanjut tentang ini di dokumentasi Travis CI .

Tetap menentukan petunjuk 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 untuk menambahkan tim Anda sendiri untuk berbagai tahap kehidupan mesin virtual. Bagian before_install dilakukan sebelum menginstal paket. Kemudian instal , yang mengikuti instalasi paket dari daftar addons.apt , yang kami tunjukkan di atas. Majelis itu sendiri berlangsung dalam sebuah naskah . Jika semuanya berjalan dengan baik, maka kita masuk ke after_success (pada bagian ini kita akan menjalankan analisis statis). Ini tidak semua langkah yang dapat diubah, jika Anda membutuhkan lebih banyak, maka Anda harus melihat dalam dokumentasi Travis CI .

Untuk memudahkan membaca, perintah dipindahkan ke skrip terpisah .travis.sh , yang ditempatkan di root proyek.

Jadi kami memiliki file .travis.yml berikut:

 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, perbarui submodul. Ini diperlukan untuk membangun PPSSPP. Tambahkan fungsi pertama ke .travis.sh (perhatikan ekstensi):

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

Sekarang kita telah datang langsung ke pengaturan PVS-Studio untuk memulai secara otomatis 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 } 

Pada awal fungsi travis_install, kami memasang kompiler yang kami perlukan menggunakan variabel lingkungan. Kemudian, jika variabel $ PVS_ANALYZE menyimpan nilai Ya (kami tentukan di bagian env selama konfigurasi matriks rakitan), kami menginstal paket pvs-studio . Selain itu, paket libio-socket-ssl-perl dan libnet-ssleay-perl juga diindikasikan, namun, mereka harus mengirim hasilnya melalui pos, jadi mereka tidak perlu jika Anda memilih metode pengiriman laporan yang berbeda.

Fungsi download_extract mengunduh dan membongkar arsip yang ditentukan:

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

Sudah waktunya untuk menyusun proyek. Ini terjadi di bagian skrip :

 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 } 

Bahkan, ini adalah konfigurasi asli yang disederhanakan, dengan pengecualian dari baris-baris ini:

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

Di bagian kode ini, kami menetapkan flag ekspor dari perintah kompilasi untuk cmake . Ini diperlukan untuk penganalisa kode statis. Anda dapat membaca lebih lanjut tentang ini di artikel " Cara menjalankan PVS-Studio di Linux dan macOS ".

Jika perakitan berhasil, maka kami menemukan diri kami di after_success , 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 mail@domain.com \ -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 perhatikan baris berikut ini secara lebih rinci:

 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 ketika mengatur variabel lingkungan Travis CI.

Baris kedua memulai analisis secara langsung. Flag -j <N> menetapkan jumlah stream untuk analisis, flag -l <file> menunjukkan lisensi, flag -o <file> mendefinisikan file untuk output log, dan flag -disableLicenseExpirationCheck diperlukan untuk versi percobaan, karena secara default pvs- studio-analyzer akan memperingatkan pengguna tentang berakhirnya lisensi. Untuk mencegah ini, Anda dapat menentukan bendera ini.

File log berisi output mentah yang tidak dapat dibaca tanpa konversi, jadi Anda harus terlebih dahulu membuat file dapat dibaca. Kami melewatkan log melalui plog-converter , dan hasilnya adalah file html.

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

Hasilnya, kami mendapat file .travis.sh berikut:

 #/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 mail@domain.com \ -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 menambahkan perubahan ke repositori git, setelah itu Travis CI akan secara otomatis memulai build. Klik "ppsspp" untuk membuka laporan perakitan:


Kami akan melihat ikhtisar dari majelis saat ini:


Jika perakitan berhasil diselesaikan, kami akan menerima email dengan hasil analisis statis. Tentu saja, pengiriman surat bukan satu-satunya cara untuk mendapatkan laporan. Anda dapat memilih metode implementasi apa pun. Tetapi penting untuk diingat bahwa setelah perakitan selesai, tidak mungkin untuk mengakses file dari mesin virtual.

Ringkasan kesalahan


Kami telah berhasil menyelesaikan bagian yang paling sulit. Sekarang mari kita pastikan bahwa semua upaya kita dibenarkan. Pertimbangkan beberapa poin menarik dari laporan tentang analisis statis, yang datang kepada saya melalui surat (bukan tanpa alasan yang saya tunjukkan).

Optimalisasi 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 panggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'sum'. Fungsi RtlSecureZeroMemory () harus digunakan untuk menghapus data pribadi. sha1.cpp 325

Fragmen kode ini terletak di modul hashing yang aman, akan tetapi, ini berisi kelemahan keamanan yang serius ( CWE-14 ). Pertimbangkan daftar assembler 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 dalam urutan yang sempurna, dan fungsi memset dijalankan, sehingga menimpa data penting dalam RAM, bagaimanapun, jangan bersukacita sejauh ini. Pertimbangkan daftar assembler dari versi Rilis dengan optimisasi:

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

Seperti dapat dilihat dari daftar, kompiler mengabaikan panggilan memset . Ini karena fungsi sha1 tidak lagi memanggil struktur ctx setelah memanggil memset . Oleh karena itu, kompiler tidak melihat titik membuang waktu prosesor menimpa memori yang tidak digunakan di masa depan. Anda dapat memperbaikinya dengan menggunakan fungsi RtlSecureZeroMemory atau serupa .

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: Ekspresi V547 'leftvol> = 0' selalu benar. sceAudio.cpp 120

Perhatikan cabang yang lain untuk yang pertama jika . Kode hanya akan dieksekusi jika semua kondisi di kiri> 0xFFFF || rightvol> 0xFFFF || leftvol <0 || rightvol <0 akan menjadi salah. Oleh karena itu, kita mendapatkan pernyataan berikut yang akan benar untuk cabang lain: leftvol <= 0xFFFF , rightvol <= 0xFFFF , leftvol> = 0 dan rightvol> = 0 . Perhatikan dua pernyataan terakhir. Apakah masuk akal untuk memeriksa apa prasyarat untuk mengeksekusi kode ini?

Jadi kita dapat dengan tenang menghapus pernyataan bersyarat ini:

 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. Di balik kondisi yang berlebihan ini ada beberapa kesalahan. Mungkin mereka memeriksa bukan apa yang diperlukan.

Ctrl + C Ctrl + V Menyerang Kembali


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

Perhatikan bagian dalam cek jika . Bukankah aneh bagi Anda bahwa kami memeriksa apakah alamat psmfData valid dua kali lipat? Jadi sepertinya aneh bagi saya ... Sebenarnya, kami memiliki, tentu saja, kesalahan ketik, dan idenya adalah untuk memeriksa kedua parameter input.

Opsi 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: Ekspresi V547 'size == 8' selalu salah. syn-att.c 195

Kesalahan ini terletak di folder ext , jadi itu tidak berlaku untuk proyek, tetapi kesalahan itu ditemukan sebelum saya perhatikan ini, jadi saya memutuskan untuk meninggalkannya. Tetap saja, artikel ini bukan tentang review kesalahan, tetapi tentang integrasi dengan Travis CI, dan tidak ada konfigurasi analisa yang dilakukan.

Ukuran variabel diinisialisasi dengan konstanta, namun, itu tidak digunakan sama sekali dalam kode, hingga pernyataan if , yang, tentu saja, mengembalikan false ketika memeriksa kondisi, karena, seperti yang kita ingat, ukurannya nol. Pemeriksaan selanjutnya juga tidak masuk akal.

Rupanya, pembuat fragmen kode lupa menimpa ukuran variabel sebelum ini.

Berhenti


Tentang ini, mungkin, kita berakhir dengan kesalahan. Tujuan artikel ini adalah untuk mendemonstrasikan pekerjaan PVS-Studio bersama dengan Travis CI, dan bukan untuk menganalisis proyek selengkap mungkin. Jika Anda menginginkan kesalahan yang semakin indah, maka Anda selalu dapat mengaguminya di sini :).

Kesimpulan


Menggunakan layanan web untuk membangun proyek bersama dengan praktik analisis tambahan dapat mendeteksi banyak masalah segera setelah penggabungan kode. Benar, satu perakitan mungkin tidak cukup, sehingga pengaturan pengujian bersama dengan analisis statis akan secara signifikan meningkatkan kualitas kode.

Tautan yang bermanfaat





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

Source: https://habr.com/ru/post/id464645/


All Articles