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

PPSSPP

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

Pendahuluan


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

PPSSPP adalah emulator konsol game PSP. Program ini mampu meniru peluncuran game apa pun dengan gambar cakram yang dirancang untuk Sony PSP. Program ini dirilis pada 1 November 2012. PPSSPP didistribusikan di bawah lisensi GPL v2. Siapa pun dapat melakukan perbaikan pada kode sumber proyek.

PVS-Studio - penganalisa kode statis untuk mencari kesalahan dan potensi kerentanan dalam kode program. Pada artikel ini, kami akan meluncurkan PVS-Studio di cloud alih-alih secara lokal di komputer pengembang untuk berbagai tujuan dan akan mencari kesalahan di PPSSPP.

Travis ci mengatur


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

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



Untuk pengujian, saya membuat garpu PPSSPP.

Kami mengaktifkan repositori yang ingin kami bangun:



Saat ini, Travis CI tidak dapat membangun proyek kami karena tidak ada instruksi untuk membangunnya. Itu sebabnya saatnya untuk konfigurasi.

Selama analisis kita akan memerlukan beberapa variabel, misalnya, kunci untuk PVS-Studio, yang tidak diinginkan untuk ditentukan dalam file konfigurasi. Jadi, mari kita tambahkan variabel lingkungan dengan mengkonfigurasi 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, Anda tidak perlu menentukannya.

Jadi, kami telah menambahkan variabel lingkungan yang kami butuhkan:


Sekarang mari kita membuat file .travis.yml dan meletakkannya di root proyek. PPSSPP sudah memiliki file konfigurasi untuk Travis CI, namun itu terlalu besar dan tidak cocok untuk contoh, jadi kami harus menyederhanakannya dan hanya menyisakan elemen dasar.

Pertama, mari kita tentukan bahasa pemrograman, versi Ubuntu Linux yang ingin kita gunakan pada mesin virtual, dan paket yang diperlukan untuk membangun:

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

Sekarang tentukan matriks bangunan:

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

Satu-satunya hal yang harus dilakukan adalah menentukan instruksi pembangunan spesifik 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 perintah Anda sendiri untuk berbagai tahap kehidupan mesin virtual. Bagian before_install berjalan sebelum menginstal paket. Kemudian instal , yang mengikuti instalasi paket dari daftar addons.apt yang telah kami tentukan di atas. Build itu sendiri terjadi dalam skrip . Jika semuanya telah berhasil, kita masuk ke after_success (di sinilah kita akan memulai analisis statis). Ini bukan semua langkah yang dapat Anda modifikasi, jika Anda membutuhkan lebih banyak, Anda harus melihat dalam dokumentasi tentang Travis CI .

Untuk kenyamanan membaca perintah dimasukkan ke 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, mari kita perbarui submodules. Ini diperlukan untuk membangun PPSSPP. Tambahkan fungsi pertama ke .travis.sh (perhatikan ekstensi):

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

Sekarang kami datang langsung untuk menyiapkan peluncuran otomatis PVS-Studio di Travis CI. Pertama, kita perlu menginstal paket PVS-Studio ke dalam 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 kita menginstal kompiler yang kita perlukan menggunakan variabel lingkungan. Kemudian, jika variabel $ PVS_ANALYZE menyimpan nilai Ya (kami tentukan di bagian env ketika mengkonfigurasi matriks build), kami menginstal paket pvs-studio . Selain itu, ada juga paket libio-socket-ssl-perl dan libnet-ssleay-perl , tetapi mereka diperlukan untuk mengirim hasilnya melalui surat, jadi mereka tidak perlu jika Anda memilih cara lain untuk pengiriman laporan.

Fungsi download_extract mengunduh dan membongkar arsip yang ditentukan:

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

Saatnya membangun 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 } 

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

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

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

Jika build berhasil, kita akan sampai ke after_success tempat kita akan menjalankan 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 secara 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 konfigurasi variabel lingkungan Travis CI.

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

File log berisi output yang tidak diproses yang tidak dapat dibaca tanpa konversi, jadi pertama-tama Anda harus membuat file tersebut dapat dibaca. Mari kita jalankan log melalui plog-converter dan dapatkan file html di output.

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

Hasilnya adalah 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; 

Saatnya untuk menambahkan perubahan ke repositori git, dan kemudian Travis CI akan secara otomatis memulai pembuatan. Klik "ppsspp" untuk membuat laporan:


Kami akan melihat gambaran umum bangunan saat ini:



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

Tinjauan singkat kesalahan


Kami telah berhasil menyelesaikan bagian yang paling sulit. Mari kita sekarang memastikan bahwa semua upaya kita telah dibenarkan. Mari kita pertimbangkan beberapa poin menarik dari laporan analisis statis yang datang kepada saya melalui surat (bukan apa-apa yang saya tentukan).

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 aman, tetapi berisi cacat keamanan serius ( CWE-14 ). Mari kita pertimbangkan daftar assembler yang dihasilkan ketika versi Debug mengkompilasi:

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

Semuanya baik-baik saja dan fungsi memset dijalankan, sehingga menghapus data penting dalam RAM, tetapi Anda tidak harus senang lagi. Mari kita pertimbangkan daftar assembler dari versi Rilis dengan optimisasi:
 ; 354 : ; 355 : memset( sum, 0, sizeof( sum ) ); ; 356 :} 

Seperti yang dapat Anda lihat dari daftar, kompiler mengabaikan panggilan memset . Ini terkait dengan fakta bahwa fungsi sha1 tidak lagi memanggil struktur ctx setelah memanggil memset . Itu sebabnya kompiler tidak melihat adanya arti membuang waktu prosesor pada menimpa memori yang tidak digunakan di masa depan. Anda dapat memperbaikinya dengan menggunakan fungsi RtlSecureZeroMemory atau fungsi serupa .

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 akan dieksekusi hanya jika semua kondisi tersisa> 0xFFFFF || rightvol> 0xFFFF || leftvol <0 || rightvol <0 salah. Oleh karena itu, kita mendapatkan pernyataan berikut yang akan benar untuk cabang lain: leftvol <= 0xFFFFF, rightvol <= 0xFFFFF, leftvol> = 0 dan rightvol> = 0 . Perhatikan dua pernyataan terakhir. Apakah masuk akal untuk memeriksa kondisi apa yang diperlukan untuk mengeksekusi fragmen kode ini?

Jadi kami dapat dengan tenang menghapus operator 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 belakang kondisi yang berlebihan ini ada beberapa kesalahan. Mungkin kita sudah memeriksa apa yang tidak kita butuhkan ...

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

Catat cek di dalam jika . Bukankah aneh bagi Anda bahwa kami memeriksa apakah alamat psmfData valid dua kali lipat? Jadi saya merasa aneh ... Sebenarnya, kami memiliki kesalahan cetak sebelum kami, tentu saja, dan idenya adalah untuk memeriksa kedua parameter input.

Varian yang benar adalah:

 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 benar-benar berlaku untuk proyek, tetapi kesalahan itu ditemukan sebelum saya menyadarinya, jadi saya memutuskan untuk menyimpannya. Namun, artikel ini bukan tentang tinjauan kesalahan tetapi tentang integrasi dengan Travis CI dan tidak ada konfigurasi penganalisa yang dilakukan.

Variabel ukuran diinisialisasi dengan konstanta, tetapi tidak digunakan sama sekali dalam kode hingga jika operator yang, tentu saja, menghasilkan informasi palsu sambil memeriksa kondisi karena, seperti yang kita ingat, ukurannya sama dengan nol. Pemeriksaan selanjutnya juga tidak masuk akal.

Rupanya, pembuat fragmen kode lupa untuk menimpa variabel ukuran sebelum itu.

Berhenti


Di situlah kita akan berhenti dengan kesalahan. Tujuan artikel ini adalah untuk menunjukkan bagaimana PVS-Studio bekerja dengan Travis CI dan tidak menganalisis proyek selengkap mungkin. Jika Anda ingin kesalahan yang lebih besar dan lebih indah, Anda selalu dapat melihatnya di sini :).

Kesimpulan


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

Tautan yang bermanfaat


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


All Articles